[Swift 문법] Swift 공식 문서 정리 - 3 - Strings and Characters(문자와 문자열)

윤여송·2022년 9월 13일
0

Swift

목록 보기
3/28
post-thumbnail

Strings and Characters

String(문자열)은 "Hello World"와 같이 Character(문자)들이 합쳐진 것이라고 불 수 있다.
Swift에서 문자열 및 문자 타입은 코드에서 텍스트를 유니코드 호환 방법으로 제공하고 문법적인 부분은 C와 비슷하다. 문자열의 연결 + 연사자를 사용하여 수행할 수 있다.물론 문자열도 상수와 변수로 선언하여 변경 가능성을 관리해 주어야 한다.문자열 보간을 통해 어떠한 문자열에서 다른 문자열을 불러올 수도 있다. Swift의 문자열은 유니코드 문자로 구성된다고 한다.


String Literals

개발자는 미리 정의되어 있는 String 값으로 문자열을 사용할 수 있다.
문자열을 사용할 때는 두 개의 " 안에 원하는 문자열을 써주면 된다.

let someString = "Some string literal value"

Multiline String Literals

만약 문자열을 여러 줄에 쓰고 싶으면 """를 사용해서 쓸 수 있다.

let quotation = """
The White Rabbit put on his spectacles. "Where shall I begin, please your Majesty?" he asked.

"Begin at the beginning, " the King said gravely, "and go on till you come to the end; then stop."
"""

여기서 """ 안의 문장은 특별한 명령어가 없으면 줄바꿈이 발생하지 않는데, 만약 줄바꿈을 사용하고 싶다면 (백슬래시)를 사용하면 된다.

let softWrappedQuotation = """
The White Rabbit put on his spectacles. "Where shall I begin, \please your Majesty?" he asked.
"Begin at the beginning," the King said gravely, "and go on \till you come to the end; then stop."
"""

Special Characters in String Literals

문자열은 다음과 같은 특수문자들을 포함할 수 있다.
\0(널문자)
\(백슬래시를 문자열에 쓰고 싶을 때),
\t(탭 키 한 번 누른 것과 동일한 효과), \n(줄바꿈),
\r(carriage return으로 커서의 위치를 줄의 맨 처음으로 보내는 기능),
\"(문자열에서 큰따옴표 쓰고 싶을 때),
\'(문자열에서 작은따옴표 쓰고 싶을 때) 또한 유니코드를 사용하고 싶으면 \u{n}을 써주면 되고 n에는 1~8자리의 16진수를 써주면 된다.

let wiseWords = "\"Imagination is more imporatant than knowledge\" - Einstein"
let dollarSign = "\u{24}"
let blackHeart = "\u{2665}"
let sparklingHeart = "\u{1F496}"

"""를 사용해서 여러 줄에 문자열을 쓸 경우에 "는 그냥 쓸 수 있다. 근데 만약 """를 쓰고 싶다면 아래와 같이 하면 된다.

let threeDoubleQuotationMarks = """
Escaping the first quotatioin mark \"""
Escaping all three quotation marks\"\"\"
"""

Extended String Delimiters

아까와 같이 특수문자를 쓰는 게 귀찮거나 \n 과 같은 문자를 쓰고 싶다면 # 안에 문자열을 넣어주면 된다. 그러면 그냥 그 문자열 그대로를 사용할 수 있다. 근데 만약 #를 쓰고 그 안에 \n과 같은 기능을 쓰고 싶다면 #개수와 같은 수의 #를 \n에 써주면 되는데 이는 예를 보면 이해가 된다.

let text = #"Line 1\nLine2"# //Line1 \nLine2 출력
let text = #"Line1 \#nLine 2"# //Line1 개행 Line2 출력
let text = ###"Line 1\###nLine2"### //Line1 개행 Line2 출력

여러 줄에 문자열을 쓸 때에도 동일하게 사용할 수 있다.

let threeMoreDoubleQuotationMarks= #"""
Here are three more double quotes: """
"""#

Initializing an Empty String

빈 문자열을 만들 때 두가지 방법이 있는데 아래와 같다.

var emptyString = "" //empty string literal
var anotherEmptyString = String() // initializer syntax
//these two strings are both empty. and are equivalent to each other

또한 string 값은 isEmpty라는 프로퍼티를 가지는데 이것은 문자열이 비어있니?라고 하는 질문을 Boolean 값으로 대답한 값이라고 볼 수 있다.

if emptyString.isEmpty{
	print("Nothing to see here")
}
// Prints "Nothing to see here"

String Mutability

String을 변수로 선언하면 수정할 수 있고, 상수로 선언하면 수정할 수 없다.

var variableString = "Horse"
variableString += "and carriage"
// variableString is now "Horse and carriage"

let constantString = "Highlander"
constantString += "and another Highlander"
// this reports a compile-time error - a constant string cannot be modified

Strings Are Value Types

Swift의 String 타입은 value 타입, 즉 값 타입이다. 만약 개발자가 새로운 String 값을 만들어 내면 그 값이 함수나 메서드에 사용될 때 복사가 되어 사용된다. 즉 아예 기존의 값과는 다른 새로운 값이 만들어진다고 볼 수 있다. 물론 복사를 한 것이기 때문에 내용은 같다. 값 타입이기 때문에 함수나 메서드로 전달된 문자열은 직접적으로 수정하지 않으면 수정되지 않을 것이라는 확신을 가질 수 있다. Swift 컴파일러는 문자열 사용을 최적화하여 실제 복사가 꼭 필요한 경우에만 이루어진다고 한다. 전달된 문자열이 수정이 되거나 하는 복사가 꼭 필요한 경우에만 복사를 한다는 말이다.


Working with Characters

String을 for-in 구문과 함께 사용하면 한 글자씩 사용할 수 있다.

for character in "Dog!!" {
	print(character)
}
//D
//o
//g
//!
//!

그리고 만약에 한 글자만 사용할 것이라면 아래와 같이 선언해 줄 수도 있다.

let exclamationMark: Character = "!"

String 값은 Character 배열로도 만들 수 있다.

let catCharacters: [Character] = ["C", "a", "t", "!", "!"]
let catString = String(catCharacters)
print(catString)
//Prints "Cat!!"

Concatenating Strings and Characters

String은 + 연산자를 사용해서 새로운 String을 만들 수 있다.

let string1 = "hello"
let string2 = "there"
var welcome = string1 + string2
//welcome now equals "hello there"

+= 연산자도 사용할 수 있다.

var instruction = "look over"
instruction += string2
//instruction now equals "look over there"

Character 값을 String 값에 추가하고 싶을 때는 string 타입의 append() 메서드를 사용하면 된다.

let exclamationMark : Character = "!"
welcome.append(exclamationMark)
//welcome now equals "hello there!"

당연한 말이지만 Character은 무조건 한 글자만 존재해야 하므로 더하거나 append를 할 수 없다.

let badStart = """
one
two
"""
let end = """
three
"""
print(badStart + end)
//Prints two lines:
//one
//twothree

let goodStart = """
one 
two

"""
print(goodStart + end)
//Prints three lines:
//one
//two
//three

여러 줄 String을 사용할 때는 마지막 줄에서는 개행이 일어나지 않기 때문에 위와 같이 해줘야 원하는 대로 문자열을 합칠 수 있다.


String Interpolation

String Interpolation은 문자열 보간이라고 해석되고 이는 String에 상수, 변수, 리터럴, 연산 등의 값을 넣는 것을 말한다. 문자열 보간을 사용할 때는 ()를 문자열에 넣어주면 되는데 예를 보자.


Counting Characters

String은 count라는 프로퍼티로 해당 문자열이 포함되는 문자의 수를 나타낼 수 있다.

let unusualMenagerie = "Koala, Snail, Penguin, Dromedary"
print("unusualMenagerie has \(unusualMenagerie.count)characters")
//Prints "unusualMenagerie has 40 characters"

아까 본 Extende grapheme clusters에서 é는 두개의 스칼라로 이루어 졌다고 했는데 이런 문자가 포함된 String의 count 프로퍼티의 값을 얼마일까?`

var word = "cafe"
print("the number of characters in \(word) is \(word.count)")
// Prints "the numbers of characters in cafe is 4.

word += "\u{301}"

print("the number of characters in \(word) is \(word.count)")
//Prints "the number of characters in café is 4"

영향을 주지 않는다는 것을 볼 수 있다.
이런 경우에서 볼 수 있는 것은 문자들이 다른 양의 메모리를 요구할 수 있다는 것을 의미한다.
즉 count 프로퍼티에 의해 반환되는 문자 수는 NSString의 length 프로퍼티와는 동일하지 않을 수 있다.
NSStirng의 length 프로퍼티는 UTF-16 표현 내 16비트 코드 단위수로 문자의 수를 센다.


Accessing and Modifying a String

String의 메서드와 프로퍼티를 사용해서 문자열에 접근하고 수정할 수 있다.

string Indices

String 타인은 index 타입이다. 즉, String.Index로 해당 위치에 있는 Character에 접근할 수 있다.
그런데 위에서 말했듯 각각의 문자가 다른 양의 메모리를 가질 수 있기 때문에 Swift 문자열은 정수 값으로 Index를 생성할 수 없다.
우선 startIndex는 String의 첫 Character에 접근하는 위치이며 endIndex는 마지막 Character에 접근하는 위치이다. 만약 빈 String 이라면 startIndex와 endIndex의 값은 같다. String의 index(before:), index(after:)을 사용해서 인덱스에 접근할 수 있다. index(:offsetBy:)메서드를 이용해서 여러 문자를 한 번에 뛰어넘을 수 도 있다.

let greeting = "Guten Tag!"
greeting[greeting.startIndex]
//G
greeting[greeting.index(before: greeting.endIndex)]
//!
greeting[greeting.index(after: greeting.startIndex)]
//u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
//a

만약 String의 범위에서 벗어난 index에 접근하려고 하면 당연하게도 에러를 발생시킨다.

greeting[greeting.endIndex]//Error
greeting.index(after: greeting.endIndex)//Error

String의 indices 프로퍼티를 통해 문자열의 모든 인덱스에 접근할 수 있다.

for index in greeting.indices{
	print("\(greeting[index]", terminator:"")
}
//Prints "G u t e n T a g !"

Collection 프로토콜로 만들어진 모든 타입은 위에서 나온 index(before:), index(after:), index(_:offsetBy:)를 사용할 수 있다. Collection타입에는 String, Array, Dictionary, Set이 있다.


Inserting and Removing

String의 특정 인덱스에 Character를 넣거나 삭제할 수 있다.
특정 인덱스에 하나의 문자만 넣을 때는 insert(_at:)메소드를 사용하고 특정 인덱스에 문자열을 넣을 때는 insert(contentsOf:at:)을 사용하면 된다.

var welcome = "hello"
welcome.insert("!", at:welcome.endIndex)
//welcome now equals "hello!"

welcome.insert(contentsOf: "there", at:welcome.index(before: welcome.endIndex))
//welcome now equals "hello there!"

삭제도 마찬가지인데 특정 인덱스의 하나의 문자만 삭제할 때는 remove(at:)을 사용하면 되고 문자열이나 특정한 범위를 삭제하고 싶을 땐 removeSubrange(_:)메소드를 사용하면 된다.

welcome.remove(at: welcome.index(before:welcome.endIndex))
//welcome now equals "hello there"

let range = welcome.index(welcome.endIndex, offsetBy:-6)..<welcome.endIndex
welcome.removeSubrange(range)
//welcome now equals "hello"

물론 이러한 메소드들도 아까 말한 Collection타입에서 모두 사용할 수 있다.


Substrings

String에서 prefix(_:)와 같은 메소드로 substring을 가지고 오면 결과는 Substring 인스턴스가 된다. Swift에서 Substring과 String은 거의 비슷하기 때문에 개발자는 동일하게 사용할 수 있다.
하지만 substring은 String과 다르게 String에 대한 작업을 수행하는 아주 짧은 시간동안만 사용한다. 만약 더 길게 사용하고 싶다면 substring을 아예 새로운 String으로 만들어 줘야 한다.

let greeting = "Hello, world!"
let index = greeting.firstIndex(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
//beginning is "Hello"

//Convert the result to a String for long-term storage
let newString = String(beginning)

문자열과 마찬가지로 substring도 메모리를 가지게 되는데 string과 substring의 차이는 여기서 발견할 수 있다.
substring은 기존의 String이 사용하고 있는 메모리를 재사용하거나 다른 substring이 사용하는 메모리를 사용할 수 있다. 이는 기존의 String이나 substring을 수정하지 않으면 메모리 복사를 하지 않기 때문에 낭비를 하지 않을 수 있다. 하지만 substring은 기존의 String의 메모리를 재사용하는데 이러한 방식 때문에 substring을 사용하는 동안에는 기존의 String을 메모리에 보관해야 한다. 즉 substring을 오래 지속하기엔 적합하지 않다.

profile
y_some__velog

0개의 댓글