*Swift Language Guide를 우리말로 풀이하고 편집한 것이다. Strings and Characters 섹션의 내용을 담고 있다.
스트링 리터럴
let constantName = "String literal value"
큰따옴표(")로 문자를 감싼 것을 스트링 리터럴(String literal)이라 한다. 스트링 리터럴을 상수나 변수의 초기값으로 할당할 수 있다. 스트링 리터럴로 초기화된 상수나 변수의 타입은 타입 추론 기능에 따라 `String`으로 추론된다.
여러 줄 스트링 리터럴
스트링 앞뒤를 각각 세 개의 큰 따옴표(""")로 감싸면 여러 줄로 이루어진 스트링 리터럴을 만들 수 있다. 스트링 리터럴 안의 줄 바꿈은 출력값에도 동일하게 반영된다.
let multilineString = """
This is the first line.
This is the second line.
This is the third line.
"""
코드 이해를 돕기 위해 줄 바꿈을 넣되 실제로 출력되지는 않게 하려면 백슬래시(\)를 사용한다.
let multilineString = """
This is the first line. \
This is the second line. \
This is the third line.
"""
// 실제로는 모두 한 줄로 출력된다
스트링은 여는 큰따옴표 다음 줄에서 시작하여 닫는 큰따옴표 앞 줄에서 끝난다. 여는 큰따옴표 뒤와 닫는 큰따옴표 앞에 만들어진 줄 바꿈은 스트링 값에 적용되지 않기 때문에 다음 두 상수도 동일하게 출력된다.
let singleLineString = "These are the same."
let multilineString = """
These are the same.
"""
스트링의 시작과 끝에 줄바꿈을 적용하려면 스트링 리터럴의 첫 줄과 마지막 줄에 빈 줄을 넣어준다.
let lineBreaks = """
This string starts with a line break.
It also ends with a line break.
"""
스트링 리터럴에 삽입할 수 있는 특수 문자
스트링 리터럴에는 이스케이프된 특수 문자(null 문자 `\0`, 백슬래시 `\\`, 가로 탭 `\t`, 줄 바꿈 `\n`, 캐리지 리턴 `\r`, 큰따옴표 `\"`, 작은따옴표 `\'`)와 유니코드 스칼라 값 `\u{n}`(n은 1~8자리 16진수)을 삽입할 수 있다.
let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// 이스케이프된 큰따옴표
let dollarSign = "\u{24}" // $, 유니코드 스칼라 U+0024
let blackHeart = "\u{2665}" // ♥, 유니코드 스칼라 U+2665
let sparklingHeart = "\u{1F496}" // 💖, 유니코드 스칼라 U+1F496
여러 줄 스트링 리터럴에는 세 개의 큰따옴표가 사용되기 때문에 스트링 리터럴 안에서는 이스케이프하지 않고(\ 없이) 큰따옴표를 바로 사용할 수 있다. 여러 줄 스트링 리터럴 안에 `"""`를 텍스트로 입력하기 위해서는 다음과 같이 한 개 이상의 큰따옴표를 이스케이프해야 한다.
let threeDoubleQuotationMarks = """
Escaping the first quotation mark \"""
Escaping all three quotation marks \"\"\"
"""
확장된 스트링 구분 기호
확장된 구분 기호(Extended delimiter) `#`으로 스트링 리터럴을 감싸 특수 문자의 본래 기능을 실행하지 않고 텍스트로서 출력할 수 있다. 예를 들어, `"Line 1\nLine 2"`는 특수 문자 `\n` 때문에 줄 바꿈이 되어 두 줄로 출력되지만, `#"Line 1\nLine 2"#`에서는 확장된 구분 기호 `#` 때문에 `\n`이 특수 문자로 인식되지 않는다. 스트링 구분 기호를 사용하면서 특수 문자도 실행하려면 양끝의 `#` 개수와 동일한 수의 `#`을 이스케이프 문자(`\`) 뒤에 삽입해야 한다. 이에 따라 `#"Line 1\#nLine 2"#`와 `###"Line1\###nLine2"###`는 동일한 형태로 줄 바꿈되어 출력된다.
여러 줄 스트링 리터럴에도 확장된 구분 기호를 사용할 수 있다. 다음과 같이 여러 줄 스트링을 `#`으로 감싸면 두 번째 `"""`이 닫는 큰따옴표로 인식되지 않고 텍스트로 출력된다.
let threeMoreDoubleQuotationMarks = #"""
Here are three more double quotes: """
"""#
빈 스트링 초기화하기
var emptyString = "" // 빈 스트링 리터럴
var anotherEmptyString = String() // 이니셜라이저 구문
스트링의 시작 지점으로 사용할 빈 `String` 값을 만들려면 변수에 빈 스트링 리터럴을 할당하거나 이니셜라이저 구문(Initializer syntax)으로 새로운 `String` 인스턴스를 초기화한다. 불리언 `isEmpty` 속성을 사용하여 빈 스트링인지 확인할 수 있다.
if emptyString.isEmpty {
print("Nothing to see here")
}
스트링의 변경 가능성
변수에 할당된 스트링은 이후에 새로운 값으로 변경이 가능하지만 상수에 할당된 스트링은 변경할 수 없다.
문자 활용하기
`for-in` 반복을 이용하여 스트링의 모든 문자에 접근할 수 있다.
for character in "Dog!🐶" {
print(character)
}
// D
// o
// g
// !
// 🐶
문자 한 개로 이루어진 스트링 리터럴을 값으로 할당하고 `Character`라는 타입 주석을 추가하여 `Character` 상수/변수를 만들 수 있다. `Character` 타입은 단 하나의 문자만 값으로 가질 수 있다.
let exclamationMark: Character = "!"
여러 개의 `Character` 값으로 이루어진 배열을 스트링 이니셜라이저에 인수로 전달하여 `String` 값을 만들 수도 있다.
let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"]
let catString = String(catCharacters)
print(catString)
// Cat!🐱 출력
스트링과 문자 연결하기
더하기 연산자(`+`)로 여러 개의 `String` 값을 연결하여 새로운 `String` 값을 만들 수 있다.
let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome의 값은 "hello there"
더하기 대입 연산자(`+=`)로 기존의 `String` 변수에 새로운 `String` 값을 덧붙일 수도 있다.
var instruction = "look over"
let additionalString = " there"
instruction += additionalString
// instruction의 값은 "look over there"
`String` 타입의 `append()` 메서드를 활용하여 `String` 변수에 `Character` 값을 추가할 수 있다. 그러나 `Character` 타입은 단 하나의 문자만 값으로 가질 수 있기 때문에 `Character` 변수에 다른 문자나 스트링을 추가하는 것은 불가능하다.
var welcome = "hello there"
let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// welcome의 값은 "hello there!"
여러 줄 스트링 리터럴을 다른 스트링과 연결할 때에는 줄 바꿈에 유의해야 한다. 다른 스트링과 마찬가지로 여러 줄 스트링 역시 마지막 줄에서 줄 바꿈을 하지 않는다. 다른 스트링과 연결할 경우, 줄 바꿈 없이 바로 연결되기 때문에 줄바꿈을 원한다면 마지막 줄로 빈 줄을 추가해야 한다.
스트링 보간
스트링 보간(String interpolation)은 스트링 리터럴 안에 상수나 변수, 리터럴, 식 등을 삽입하여 새로운 `String` 값을 만드는 것이다. 한 줄 스트링 리터럴과 여러 줄 스트링 리터럴 모두 스트링 보간을 사용할 수 있다. 스트링 보간 방식은 간단하다. 삽입할 항목을 괄호로 감싸고 그 앞에 백슬래시를 추가하면 된다.
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message의 값은 "3 times 2.5 is 7.5"
확장된 스트링 구분 기호(`#`)로 스트링을 감싸면 스트링 보간이 되지 않고 특수 기호를 비롯한 모든 문자가 그대로 출력된다. `#"This \(multiplier) is not recognized."#`라는 코드에서 `multiplier`의 값은 적용되지 않고 문자 그대로 출력된다. 확장된 스트링 구분 기호를 사용하면서 스트링 보간까지 함께 하려면 양끝의 `#` 개수와 동일한 수의 `#`을 백슬래시 뒤에 삽입해야 한다. `#"6 times 7 is \#(6 * 7)."#`와 같이 적용할 수 있다.
유니코드(Unicode)
유니코드는 세계 각국의 언어를 통일된 방식으로 표현하기 위해 사용하는 국제 표준 문자부호 체계이다. 유니코드를 통해 지구상의 거의 모든 언어를 표준화된 양식으로 표현할 수 있으며 텍스트 파일이나 웹 페이지 같은 외부 소스로부터 문자를 읽어 들이거나 외부 소스에 문자를 작성할 수 있다. Swift의 `String`과 `Character` 타입은 유니코드를 완전히 지원하는 데이터 타입이다.
유니코드 스칼라 값
Swift의 `String` 타입은 유니코드 스칼라 값(Unicode scalar value)으로 만들어졌다. 유니코드 스칼라 값은 각 문자나 수정자에 사용되는 고유한 21비트 수를 말한다. 예를 들면 소문자 'a'의 스칼라 값은 U+0061, 병아리 아이콘(🐥)의 스칼라 값은 U+1F425이다. 모든 21비트 스칼라 값이 문자에 할당되는 것은 아니다. 일부 스칼라 값은 미래의 할당이나 UTF-16 인코딩을 위해 예약되어 있다. 문자에 할당된 스칼라 값은 대개 `LATIN SMALL LETTER A`(U+0061), `FRONT-FACING BABY CHICK`(U+1F425) 같은 이름을 가진다.
확장된 문자소 클러스터
`Character` 타입의 각 인스턴스는 하나의 확장된 문자소 클러스터(Extended grapheme cluster)를 나타낸다. 확장된 문자소 클러스터는 한 개 이상의 유니코드 스칼라를 결합한 것으로, 여러 개의 유니코드 스칼라가 모여 사람이 읽을 수 있는 하나의 문자를 만들어 낸다. 프랑스어 알파벳 é를 예로 들어보자. é는 구성 요소에 따라 하나의 스칼라로 이루어진 문자소 클러스터가 될 수도 있고 여러 개의 스칼라로 이루어진 문자소 클러스터가 될 수도 있다. 유니코드 스칼라 `é`를 사용한 경우(유니코드 스칼라 값 U+00E9, LATIN SMALL LETTER E WITH ACUTE) 하나의 스칼라로 이루어진 문자소 클러스터이며, 문자 `e`(유니코드 스칼라 값 U+0065, LATIN SMALL LETTER E)와 양음 악센트 `´`(유니코드 스칼라 값 U+0301, COMBINING ACUTE ACCENT)를 결합한 경우 두 개의 스칼라로 이루어진 문자소 클러스터이다. 양음 악센트는 유니코드를 인식하는 텍스트 렌더링 시스템을 통해 앞의 문자에 결합된다. 이를 코드로 나타내면 다음과 같다.
let eAcute: Character = "\u{E9}" // é
let combinedEAcute: Character = "\u{65}\u{301}" // e + ́
확장된 문자소 클러스터는 여러 개의 복잡한 스크립트 문자를 하나의 `Character` 값으로 나타내는 데 용이하다. 한글 음절 또한 구성된 형태와 분해된 형태로 나타낼 수 있다.
let precomposed: Character = "\u{D55C}" // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ + ᅡ + ᆫ
// precomposed와 decomposed의 값은 모두 '한'이다
확장된 문자소 클러스터를 통해 인클로징 부호(예: U+20DD, COMBINING ENCLOSING CIRCLE)와 다른 유니코드 스칼라를 결합하여 하나의 `Character` 값을 만들 수도 있다.
let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute의 값은 é⃝
국가 표시 기호를 나타내는 스칼라 값을 결합하여 하나의 `Character` 값을 만들 수도 있다. 국가 표시 기호 `U`(U+1F1FA, REGIONAL INDICATOR SYMBOL LETTER U)와 국가 표시 기호 `S`(U+1F1F8, REGIONAL INDICATOR SYMBOL LETTER S)를 결합하면 🇺🇸 라는 하나의 `Character` 값이 만들어진다.
let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS의 값은 🇺🇸
문자 수 세기
`String` 타입의 `count` 속성을 이용하여 스트링 안에 있는 문자 수를 셀 수 있다.
let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
print("unusualMenagerie has \(unusualMenagerie.count) characters")
// "unusualMenagerie has 40 characters" 출력
Swift에서는 확장된 문자소 클러스터가 사용되기 때문에 스트링을 결합하거나 변경한다고 해서 문자 수가 반드시 변하는 것은 아니다. 4개의 문자로 이루어진 'cafe'라는 단어를 예로 들어보자. 'cafe'로 스트링을 초기화한 다음 양음 악센트 `´`(유니코드 스칼라 값 U+0301, COMBINING ACUTE ACCENT)를 끝에 추가한다고 해도 문자 수는 여전히 4개이다. `e`와 `´`가 결합하여 `é`라는 하나의 문자가 되기 때문이다.
var word = "cafe"
print("the number of characters in \(word) is \(word.count)")
// "the number of characters in cafe is 4" 출력
word += "\u{301}" // 양음 악센트(COMBINING ACUTE ACCENT, U+0301) 추가
print("the number of characters in \(word) is \(word.count)")
// "the number of characters in café is 4" 출력
확장된 문자소 클러스터는 여러 개의 유니코드 스칼라로 구성될 수 있고, 같은 문자라 할지라도 표현 방식에 따라 사용하는 메모리의 양이 다르다. 스트링의 문자 수를 계산하기 위해서는 스트링을 반복 실행하여 확장된 문자소 클러스터의 경계를 확인해야 한다. `count` 속성은 스트링의 유니코드 스칼라를 반복 실행하여 문자 값을 확인하므로 긴 스트링의 문자 수를 확인할 때에는 이 점에 유의해야 한다. 더불어 `count` 속성으로 반환한 문자 수는 동일한 문자로 이루어진 `NSString`의 `length` 속성이 반환하는 수와 언제나 일치하지는 않는다. `NSString`의 길이는 확장된 문자소 클러스터(유니코드)가 아니라 16비트 코드 유닛(UTF-16)의 개수를 기준으로 하기 때문이다.
스트링에 접근하고 값 수정하기
스트링의 메서드와 속성을 이용하거나 서브스크립트 문법을 이용하여 스트링에 접근하고 값을 수정할 수 있다.
스트링 인덱스
모든 `String` 값은 `String.index`와 같은 형식의 인덱스 타입을 가지고 있다. Swift에서는 인덱스를 정수로 매기지 않고 `startIndex`, `endIndex` 같은 속성을 사용한다. `startIndex`는 스트링의 첫 번째 문자를 가리키며 `endIndex` 속성은 스트링의 마지막 문자 다음을 가리킨다. 빈 스트링의 경우, `startIndex`와 `endIndex`는 동일하다.
`String.index(before:)`와 `String.index(after:)` 메서드를 이용하여 특정 인덱스 앞뒤에 있는 문자에 접근할 수 있다.
스트링 비교하기
Swift에서는 스트링과 문자의 같음 비교, 접두사의 같음 비교, 접미사의 같음 비교를 통해 텍스트 값을 비교할 수 있다.
스트링과 문자의 같음
같음 연산자(`==`)와 같지 않음 연산자(`!=`)를 이용해 스트링/문자 값이 같은지 확인할 수 있다.
let quotation = "We're a lot alike, you and I."
let sameQuotation = "We're a lot alike, you and I."
if quotation == sameQuotation {
print("These two strings are considered equal")
}
// "These two strings are considered equal" 출력
두 `String` 값 또는 두 `Character` 값의 확장된 문자소 클러스터가 본질적으로 동일한 경우(유니코드 스칼라가 다르다 해도 언어적 의미와 모양이 동일한 경우), 텍스트 값도 동일한 것으로 간주된다. 예를 들어 알파벳 `é`는 유니코드 스칼라 값 `U+00E9`(LATIN SMALL LETTER E WITH ACUTE)와 유니코드 스칼라 값 `U+0065`(LATIN SMALL LETTER E) + `U+0301`(COMBINING ACUTE ACCENT)라는 두 가지 방식으로 나타낼 수 있는데, 두 표현의 확장된 문자소 클러스터는 본질적으로 동일하다.
이와 반대로 영어 알파벳 `A`(U+0041, LATIN CAPITAL LETTER A)와 러시아어 알파벳 `A`(U+0410, CYRILLIC CAPITAL LETTER A)는 동일한 것으로 간주되지 않는다. 문자의 모양은 유사하나 언어적 의미가 다르기 때문이다.
접두사와 접미사의 같음
스트링 타입의 `hasPrefix(_:)`와 `hasSuffix(_:)` 메서드를 호출하여 스트링에 특정한 접두사나 접미사가 있는지 확인할 수 있다. 두 메서드는 하나의 스트링을 인수로 받고 불리언 값을 반환한다. 아래 예시에서는 두 메서드를 이용하여 배열 안에 있는 특정 문구의 개수를 찾는다.
let romeoAndJuliet = [
"Act 1 Scene 1: Verona, A public place",
"Act 1 Scene 2: Capulet's mansion",
"Act 1 Scene 3: A room in Capulet's mansion",
"Act 1 Scene 4: A street outside Capulet's mansion",
"Act 1 Scene 5: The Great Hall in Capulet's mansion",
"Act 2 Scene 1: Outside Capulet's maansion",
"Act 2 Scene 2: Capulet's orchard",
"Act 2 Scene 3: Outside Friar Lawrence's cell",
"Act 2 Scene 4: A street in Verona",
"Act 2 Scene 5: Capulet's mansion",
"Act 2 Scene 6: Friar Lawrence's cell"
] // <로미오와 줄리엣>의 장면별 배경을 담은 배열
var act1SceneCount = 0
for scene in romeoAndJuliet {
if scene.hasPrefix("Act 1") {
act1SceneCount += 1
}
}
print("There are \(act1SceneCount) scenes in Act 1")
// Act 1의 장면 개수를 계산하기 위해 "Act 1"을 접두사로 갖는 요소의 개수 확인
var mansionCount = 0
var cellCount = 0
for scene in romeoAndJuliet {
if scene.hasSuffix("Capulet's mansion") {
mansionCount += 1
} else if scene.hasSuffix("Friar Lawrence's cell") {
cellCount += 1
}
}
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
// Capulet mansion과 Friar Lawrence cell을 배경으로 하는 모든 장면 개수를 계산하기 위해 "Capulet's mansion"과 "Friar Lawrence's cell"을 접미사로 갖는 요소의 개수 확인
`hasPrefix(_:)`와 `hasSuffix(_:)` 메서드 역시 각 접두사/접미사의 확장된 문자소 클러스터가 본질적으로 동일한지 검사한다. 유니코드 표현 방식이 달라도 본질적으로 동일하면 같은 것으로 간주된다.
'Swift 언어 가이드' 카테고리의 다른 글
Swift 언어 가이드 - 기본 연산자 (0) | 2020.04.14 |
---|---|
Swift 언어 가이드 - 기초 (0) | 2020.04.02 |