본문 바로가기
Swift 공식 가이드/Swift 4

Strings and Characters

by 토끼찌짐 2018. 6. 20.

Apple 제공 Swift 프로그래밍 가이드(4.2)의 Strings and Characters 부분을 공부하며 정리한 글입니다. 개인적인 생각, 이해를 돕기 위한 예제도 조금 들어가있습니다.


들어가며

String 은 문자열을, Character 는 문자를 저장할 수 있는 타입입니다. Swift String 은 단순한 문법으로 다룰 수 있으며, 그럼에도 불구하고 빠릅니다. 특히 String 두 개를 연결할 때 + 연산자만 사용하면 되는데, 이 부분은 Objective-C 에 비하면 참 편해졌습니다.

String 에 특정 상수/변수 등을 포함해야 할 때는 Objective-C 와 다르게 String Literal 안에 \(변수이름)을 넣어야 합니다. 이게 무슨 말인지는 String Interpolation 부분에서 자세히 알려드리겠습니다.

Swift 에서의 모든 String 은 encoding-independent Unicode character 로 구성되어 있으며, 유니코드 호환도 잘 됩니다.

NOTE

Swift’s String type is bridged with Foundation’s NSString class. Foundation 모듈이 String 의 extension 을 구현해놓았기 때문에, 현재는 Foundation 만 import 하면 String 으로도 NSString 의 메서드들을 사용할 수 있습니다.

Foundation 과 Cocoa 사이에서의 String 사용 방법에 대한 더 자세한 정보는 Bridging Between String and NSString 을 참고하세요.




String Literals

String Literal 이란 double quotes( )로 둘러싸인 문자열을 말합니다. 이것을 통해 미리 String 값을 define 해놓고, String 상수/변수에 그 값을 할당할 수 있습니다.

let someString = "Some string literal value" // String Literal로 초기화된 변수는 String 타입 (타입유추됨)
let lenghtOneString = "a" // 이 경우도 변수는 String 타입. Character 타입이라고 명시하면 Character 타입이 된다.
cs


Multiline String Literals

만약 여러 라인으로 나눠서 입력할 String Literal 이 필요하다면 그 시작과 끝을 three double quotation marks( """ )로 감싸세요.

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 singleLineString = "These are the same."
let multilineString = """
These are the same.
"""
 
 
// backslash (\) 를 써주면 String 값에서는 그 라인에서 자동 줄바꿈되지 않습니다
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."
"""
 
 
// 이 경우 앞뒤로 \n(라인 피드)가 들어갑니다
let lineBreaks = """
 
This string starts with a line break.
It also ends with a line break.
 
"""
cs


멀티라인 String 은 들여쓰를 할 수 있습니다. 들여쓰기를 하려면 마침큰따옴표(closing quotation marks, """ )전에 공백(whitespace)을 넣으세요. 그러면 다른 라인들도 그만큼의 공백은 String 값에 포함되지 않고 그냥 무시됩니다.


Special Characters in String Literals

String literal 은 다음의 특별한 문자를 포함할 수 있습니다:

    • escaped special characters: \0 (null character), \\ (backslash), \t (horizontal tab), \n (line feed), \r (carriage return), \" (double quotation mark) and \' (single quotation mark)

    • arbitrary Unicode scalar: \u{n}, where n is a 1–8 digit hexadecimal number with a value equal to a valid Unicode code point

예제를 봅시다. 

let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imagination is more important than knowledge" - Einstein
let dollarSign = "\u{24}"        // $,  Unicode scalar U+0024
let blackHeart = "\u{2665}"      // ♥,  Unicode scalar U+2665
let sparklingHeart = "\u{1F496}" // 💖, Unicode scalar U+1F496
 
let threeDoubleQuotationMarks = """
Escaping the first quotation mark \"""
Escaping all three quotation marks \"\"\"
"""
cs




Initializing an Empty String

빈 문자열이 들어있는 String 타입 변수를 만드는 방법을 알아봅시다:

var emptyString = "" // empty string literal을 할당하거나
var anotherEmptyString = String()  // 초기화 문법을 사용하거나
// 두 String 모두 똑같이 비어있다.
cs


String 값이 비어있는지 확인하려면 Boolean 타입인 isEmpty 프로퍼티를 체크해보세요:

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




String Mutability

변수는 값 변경 가능, 상수는 값 변경 불가의 룰이 똑같이 적용됩니다:

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
cs


Swift 에서는 이처럼 String 이라는 타입이 하나 있고, 그 타입을 상수로 만들지 변수로 만들지에 따라 Mutability가 결정됩니다. 이것은 NSString / NSMutableString 등 변수의 타입 자체로 Mutability를 결정했던 Objective-C & Cocoa 와는 다른 방식입니다.



Strings Are Value Types

Swift 의 StringValue type 입니다. 따라서 함수나 메서드로 전달되거나 새로운 상수나 변수로 할당될 때에 그 String 값은 복사 됩니다. 이 경우 String 의 참조가 아닌 그 안에 들어있는 값만 새로 복사되어 전달되는 것이기 때문에, 복사된 값을 변경해도 원본은 영향받지 않습니다. 이 Value type 이라는 것은 Structures and Enumerations Are Value Types 에서 자세히 다루게 될 텐데, 값 복사본이 전달된다는 특성을 이해하고 있어야 앞으로 원하는 코드를 작성할 수 있을 것입니다. (NSString 의 경우에는 참조가 전달되기 때문에, 기존에 Objective-C 개발을 했던 경우 참조 전달과 착각하여 실수를 하기 쉽습니다.)

<추가설명1 - Value type 의 복사에 대해> Value type 의 할당을 할 때는 값 복사가 일어난다고 했습니다. 그러나 엄밀히 말하면 할당을 하는 시점마다 항상 복사가 일어나는 것은 아닙니다. 일단 복사는 하지않고 같은 곳만 바라보고 있다가, 값이 변경될 때에 복사를 하게 됩니다. 이것은 Swift 의 컴파일러가 최적화를 해주기 때문인데요. 왜 이런 식으로 동작하는 것일까요? 예를들어 Value type 중 하나인 Array 를 생각해보겠습니다. 만약 용량이 상당히 큰 Array 가 있는데, 이것을 함수 파라미터로 넘기거나 할 때마다 항상 Array 전체 복사가 일어난다면 성능이 많이 떨어질 것입니다.

<추가설명2 - Value type 과 Reference type 의 차이점 한 가지> Value type 은 var 로 선언될 경우에만 내부값 변경이 가능하고, Reference type 은 var 와 let 어느 것으로 선언되든지 내부값 변경이 가능합니다. 예를 들어 다음과 같은 Struct 하나를 떠올려봅시다: 이 Struct 에는 Name 이라는 내부 프로퍼티가 하나 있고 Name 은 var 로 선언되어 있습니다. 단순히 생각해보면 Name 을 언제든 바꿀 수 있을 것 같은 생각이 듭니다. 그러나 Struct 는 Value type 입니다. 따라서 이 Struct 가 만약 let 으로 선언되었다면 Name 프로퍼티는 변경이 불가능합니다. (반면 Reference type 의 경우에는 var 와 let 어느 것으로 선언되더라도 내부값을 변경할 수 있습니다. 참고로 NSObject 를 상속받은 객체, 기타 Class로 선언된 객체들이 Reference type 에 속합니다.)



Working with Characters

Stringcharacters 프로퍼티를 사용하면 String 값의 각각의 문자(Character)에 접근할 수 있습니다다. for-in loop 를 이용하여 iterating 해봅시다:

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


for-in loop 에 대해서는 For-In Loops 에 자세히 기술되어 있습니다.

상수/변수를 생성할 때 Character 타입임을 명시하고 single-character string literal 을 할당하면 Character 타입을 만들 수 있습니다:

let exclamationMark: Character = "!"
cs


Character 값들로 구성된 array 를 이니셜라이저에 넘겨서 String 타입을 만들 수 있습니다:

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




Concatenating Strings and Characters

Swift 의 String 값들은 +, += 연산자를 통해 서로 더할 수 있습니다. Character 값을 String 값에 더하려면 append() 메서드를 사용해야 합니다. 예제로 알아보겠습니다:

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"
 
let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// welcome now equals "hello there!"
 
// <주의> welcome += exclamationMark 는 서로 타입이 달라서 Compile error
cs


Character 타입은 오직 하나의 문자만 그 안에 담을 수 있습니다. 따라서 Character 타입의 변수에 다른 String 이나 Character 값을 더하는 것은 불가능합니다.

var str: String = "str"
var char: Character = "c"
 
let str_str = str + str
let char_char = char + char // compile error
let str_char = str + char // compile error
let str_append_char = str.append(char)
 
let str_let: String = "str_let"
let str_let_append_char = str_let.append(char) //compile error
cs


여러 줄의 string literal 을 더하는 경우에는 마지막 라인에 line break (\n) 가 포함되어 있느냐 없느냐에 따라 결과가 달라지는 경우가 있습니다. 예제로 알아봅시다:

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
cs




String Interpolation

상수, 변수, String literal 을 조합하여 String 을 만들 수 있습니다. 넣고 싶은 아이템을 괄호 + 백슬러시( )로 묶습니다. 예를 들어 변수를 String literal 에 포함시키고 싶다면 \(변수이름) 형태로 String literal 내의 적절한 위치에 넣습니다.

let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"
cs


Note

String literal 괄호 안의 수식에는 unescaped backslash( ), carriage return, line feed 등을 포함시킬 수 없습니다. 그러나 다른 String literal 을 포함시킬 수는 있습니다.




Unicode

String 은 사실 유니코드 스칼라(Unicode scalar) 값들로 이루어져 있습니다. 유니코드 스칼라는 Character 하나하나를 위한 유니크한 21-bit Number 입니다. 예를 들어 U+0061 for LATIN SMALL LETTER A ("a"), or U+1F425 for FRONT-FACING BABY CHICK ("🐥").

Swift 의 모든 Character 인스턴스는 하나의 extended grapheme cluster 를 가집니다. Extended grapheme cluster 란, 유니코드 스칼라의 sequence 인데, combine 되면 사람이 읽을 수 있는 하나의 문자가 됩니다. 다음 예제를 봅시다:

let eAcute: Character = "\u{E9}" // é 
let combinedEAcute: Character = "\u{65}\u{301}" // e followed by ́
// eAcute is é (유니코드 스칼라 1개), combinedEAcute is é (유니코드 스칼라 2개)
// 두 개는 같은 문자!
 
let precomposed: Character = "\u{D55C}"                  // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"   // ᄒ, ᅡ, ᆫ
// precomposed is 한, decomposed is 한
 
let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute is é⃝
 
let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS is 🇺🇸
 
cs




Counting Characters

String 을 구성하는 Character 개수를 알고 싶으면 String 의 count 프로퍼티를 사용합니다:

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


참고로 Character 하나에 포함된 유니코드 스칼라가 2개 이상이라도 그와 관계없이 Character 하나 당 count 는 하나로 간주됩니다. count 프로퍼티는 Character 의 개수를 세는 것이지 유니코드 스칼라의 개수를 세는 것이 아닙니다.

var word = "cafe"
print("the number of characters in \(word) is \(word.count)")
// Prints "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)")
// Prints "the number of characters in café is 4"
cs


위 예제에서 볼 수 있는 것처럼 두 String 이 눈에는 같은 String 처럼 보일지 몰라도 그것을 구성하는 유니코드 스칼라의 개수와 종류는 다를 수도 있습니다. 따라서 똑같은 것처럼 보이는(same string's representation) 두 String 이 서로 다른 양의 메모리를 차지하고 있는 경우도 있습니다.




Accessing and Modifying a String

String 의 값에 접근하거나 변경하는 방법을 알아봅시다. String 의 메서드, 프로퍼티, 서브스크립트 문법 등을 통해 가능합니다.


String Indices

String 값을 구성하는 Character 들 각각이 String 의 어느 위치에 있는지 가리키기 위하여 index typeString.Index 가 사용됩니다.

왜 Int 값으로 Character 의 위치에 접근하지 못하고 굳이 index type 을 써야할까요? 그 이유는 위에서 설명한 유니코스 스칼라에 있습니다. Character 마다 차지하는 메모리가 다를 수 있으며, 유니코드 스칼라를 몇 개씩 건너뛰어야 다음 Character 가 나올지 미리 알 수 없기 때문에 그 역할을 해주는 index type 을 사용하는 것입니다.


String 에는 index 를 위한 다음 프로퍼티가 있습니다.

  • startIndex : 첫 번째 Character 를 가리키는 index
  • endIndex : 마지막 Character의 한 칸 뒤 index. 따라서 endIndex 에서 한 칸 앞으로 와야 마지막 Character 를 가리키게 됩니다.
String 이 비어있을 경우, startIndex 와 endIndex 는 같습니다.

String 에는 Index 를 위한 다음 메서드가 있습니다.

  • index(before:) : 바로 전
  • index(after:) : 바로 뒤
  • index(_:offsetBy:) : 오른쪽 방향으로 N만큼 떨어져있는 곳(음수면 왼쪽으로 N만큼)


String 의 특정 인덱스에 있는 Character 에 접근하기 위하여 서브스크립트 문법을 사용할 수 있습니다.

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
cs


String 의 range 를 벗어난 index 에 접근하려고 하면 런타임 에러가 발생됩니다.
greeting[greeting.endIndex] // Error
greeting.index(after: greeting.endIndex) // Error
cs


String 의 indices 프로퍼티는 String 안의 Character 들에 대한 index 의 Range 를 만들어줍니다. 이 프로퍼티를 for 문과 사용하면 String 내의 각 문자를 순회하는 코드를 간결하게 작성할 수 있습니다.

for index in greeting.indices {
    print("\(greeting[index]) ", terminator: "")
}
// Prints "G u t e n   T a g ! " // 다시 한 번 강조하지만, 위 코드에서의 index 는 0, 1 등의 number type 이 아니라 index type!
cs

NOTE
프로퍼티 startIndexendIndex, 메서드 index(before:), index(after:), 그리고 index(_:offsetBy:)Collection 프로토콜을 따르기만 한다면 어떤 타입이든 사용가능합니다. String, Array, Dictionary, Set 등의 타입이 해당됩니다.


Inserting and Removing

String 의 특정 인덱스에 Character 하나를 삽입하려면 insert(_:at:) 메서드를 사용하세요. 그리고 특정 인덱스에 다른 String 을 삽입하려면 insert(contentsOf:at:) 메서드를 사용하세요.

// String 특정 위치에 Character 삽입하기
var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome now equals "hello!"
 
// String 특정 위치에 다른 String 삽입하기
welcome.insert(contentsOf: " there", at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there!"
cs


반대로 특정 Character, String 을 제거하는 경우에는 remove(at:) 와 removeSubrange(_:) 메서드를 사용하세요.

// String에서 특정 위치의 Character 지우기
welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there"
 
// String에서 sub string 지우기
let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome now equals "hello"
cs


NOTE

RangeReplaceableCollection 프로토콜을 따르는 어떤 타입이건 insert(_:at:), insert(contentsOf:at:), remove(at:)removeSubrange(_:) 메서드를 사용할 수 있습니다. String, Array, Dictionary, Set 등 모두 포함됩니다.





Substrings

String 의 서브 스트링을 가져올 때—예를 들어, 서브스크립트를 사용하거나 prefix(_:) 같은 메서드를 사용해서—그 결과는 Substring 인스턴스입니다. 또 다른 String 이 아니라요. Swift 의 Substring 은 String 의 메서드와 똑같은 메서드들을 많이 가지고 있습니다. 즉 String 을 다룰 때와 같은 방식으로 Substring 을 다룰 수 있습니다. 그러나 Substring 은 String 을 가지고 특정 액션을 수행하는 짧은 시간에서만 사용하도록 하세요. 만약 긴 시간동안 Substring 을 저장하고 사용해야 한다면 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)
cs


Substring 도 String 과 마찬가지로 자신을 구성하는 문자열들이 저장되는 메모리를 참조하고 있습니다. 그러나 String 과 Substring 은 성능 최적화 측면에서 달라지는데요, Substring 은 Original String 을 저장하기 위해 사용되는 메모리의 일부를 재사용할 수 있습니다. 또한 다른 Substring 을 저장하기 위해 사용되는 메모리의 일부를 재사용할 수도 있습니다. (String 역시 비슷한 최적화가 이루어지지만, String 의 경우에는 두 개의 String 이 같은 메모리를 공유한다면 이 두 개의 String 은 서로 동일합니다(equal 관계에 놓여 있습니다).) 이런 성능 최적화의 효과로, String 이나 Substring 의 값을 수정하기 전까지는 메모리 복사가 이루어지지 않습니다. 즉 메모리 복사에 대한 자원을 절약할 수 있습니다. 앞서 언급했던 것처럼 Substring 은 긴 시간 사용하기에는 적절하지 않습니다(not suitable for long-term storage). 왜냐하면 Substring 은 Original String 의 메모리를 재사용하기 때문에 Substring 이 사용되는 동안에는 이 Original String 이 통째로 메모리에 남아 있어야 하기 때문입니다.


위 예제에서 greeting 은 String 입니다. 따라서 greeting 은 자신을 구성하는 문자열들이 저장되는 메모리를 참조하고 있습니다. beginninggreeting 의 Substring 이기 때문에, beginninggreeting 이 사용하는 메모리를 재사용합니다. 그와 다르게 newString 은 String 입니다. 따라서 newString 은 자신만의 메모리를 할당받습니다. 아래 그림은 이런 관계들을 보여주고 있습니다:


NOTE

StringSubstring 둘 다 StringProtocol 을 따르고 있습니다. 그러니 String 값을 조작하는 함수에 이 타입들을 편리하게 사용할 수 있습니다.




Comparing Strings


String 비교에는 세 가지 종류가 있습니다.

  • String and Character Equality : 문자열 전체 비교. 참고로 두 Character의 유니코드 스칼라의 조합에 차이가 나더라도 결과적으로 외관과 의미가 동일한 Character라면 두 Character는 같다고 취급.
  • prefix equality : 앞이 일치하는지
  • suffix equality : 뒤가 일치하는지

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")
}
 
if quotation.hasPrefix("We") {
    print("quotation start with We")
}
 
if quotation.hasSuffix("I.") {
    print("quotation end with I.")
}
cs




Unicode Representations of Strings

앞서 살펴보았듯이 String은 Character(문자열)로 구성되고, Character는 Unicode Scalar로 구성됩니다.


Unicode String 을 텍스트 파일이나 다른 저장소에 쓰게될 때, String 을 구성하는 유니코드 스칼라들은 다음 encoding forms 중 하나로 인코딩됩니다.

  • UTF-8 encoding form (인코딩을 위한 codeUnit이 8-bit)
  • UTF-16 encoding form (인코딩을 위한 codeUnit이 16-bit)
  • UTF-32 encoding form (인코딩을 위한 codeUnit이 32-bit)


앞에서는 String을 for-in loop 로 돌면서, String 을 구성하는 각각의 Character 값에 접근하는 법을 알아보았습니다. (엄밀히 말하면 Unicode extended grapheme cluster를 하나씩 for문으로 도는 것입니다.)

지금부터는 String의 값을 유니코드 표현법으로 접근하는 법을 살펴봅시다. 종류는 다음 세 가지가 있습니다.

  • UTF-8 code units의 collection (String의 utf8 프로퍼티를 통해 접근)
  • UTF-16 code units의 collection (String의 utf16 프로퍼티를 통해 접근)
  • UTF-32 code units의 collection (String의 unicodeScalars 프로퍼티를 통해 접근)


예제를 통해 하나씩 살펴봅시다.

let dogString = "Dog‼🐶"
cs

위 String 은 다음과 같은 Character 들로 구성되어 있습니다.

  • D
  • o
  • g
  • !! (DOUBLE EXCLAMATION MARK or 유니코드 스칼라 U+203C)
  • 🐶  (DOG FACE or 유니코드 스칼라 U+1F436)


이제 이 String의 값에 유니코드 표현법으로 접근해봅시다.



UTF-8 Representation


Stringutf8 프로퍼티를 사용하세요.


  • Type: String.UTF8View
  • collection of unsigned 8-bit (UInt8) values



for codeUnit in dogString.utf8 {
    print("\(codeUnit) ", terminator: "")
}
print("")
// Prints "68 111 103 226 128 188 240 159 144 182 "
cs


처음 3개 codeUnit 값 (68, 111, 103) 은 각각 D, o, g 를 표현합니다. 1 byte 만으로 표현할 수 있는 이런 문자들은 ASCII 로 표기했을 때도 동일한 코드를 가집니다. 그 다음 3개 codeUnit 값 (226, 128, 188) 은 3 byte UTF-8 이고 DOUBLE EXCLAMATION MARK 문자를 표현합니다. 마지막 4개 codeUnit 값 (240, 159, 144, 182) 는 4 byte UTF-8 이고 DOG FACE 문자를 표현합니다.



UTF-16 Representation


String 의 utf16 프로퍼티를 사용하세요.


  • Type: String.UTF16View
  • collection of unsigned 16-bit (UInt16) values



for codeUnit in dogString.utf16 {
    print("\(codeUnit) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 55357 56374 "
cs


처음 3개 codeUnit 값 (68, 111, 103) 은 각각 D, o, g 를 표현하며 이는 UTF-8 표현과 같은 값입니다. D, o, 는 ASCII character 로 표기할 수 있는 문자들이기 때문입니다.

4번째 codeUnit 값 (8252) 는 16진수 값 203C 를 10진수로 나타낸 것입니다. 유니코드 스칼라 U+203C DOUBLE EXCLAMATION MARK 문자를 표현합니다. 이 문자는 UTF-16 에서 1개의 code unit 으로 나타낼 수 있습니다.

그러나 그 다음 나오는 DOG FACE 문자는 UTF-16 에서 2개의 codeUnit 이 필요합니다. 55357(U+D83D)과 56374(U+DC36)은 DOG FACE를 나타내기 위한 UTF-16 surrogate pair 입니다.

<보충설명> surrogate pair 란? 2 byte 인 UTF-16 으로 모든 문자를 표현하기에는 부족하기 때문에, 2 byte 만으로는 표현할 수 없는 예외 문자들을 Supplementary Characters 라고 정했습니다. 이 문자를 만들기 위한 인코딩 방식을 Surrogate Pair 라고 합니다.




Unicode Scalar Representation


String 의 unicodeScalars 프로퍼티를 사용하세요.


  • Type: UnicodeScalarView
  • collection of values of type UnicodeScalar
  • 각 UnicodeScalar value 라는 프로퍼티를 가진다. (scalar의 21-bit value, 즉 Uint32 값)



for scalar in dogString.unicodeScalars {
    print("\(scalar.value) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 128054 "
cs


처음 세 개의 UnicodeScalar 값 (68, 111, 103) 은 변함없이 D, o, 입니다.

4번째 codeUnit 값 (8252) 도 변함없이 16진수 값 203C 를 10진수로 나타낸 것입니다. 유니코드 스칼라 U+203C DOUBLE EXCLAMATION MARK 문자를 표현합니다.

그 다음 UnicodeScalar 값 128054 DOG FACE 인 유니코드 스칼라 U+1F436 을 10진수로 나타낸 것입니다.

다음과 같이 UnicodeScalar 를 통해 새로운 String 값을 구성할 수도 있습니다:


for scalar in dogString.unicodeScalars {
    print("\(scalar) ")
}
// D
// o
// g
// ‼
// 🐶
cs


'Swift 공식 가이드 > Swift 4' 카테고리의 다른 글

Collection Types  (0) 2018.09.05
Basic Operators  (0) 2018.03.23
The basics  (0) 2018.03.02
A Swift Tour  (0) 2017.10.17
Version Compatibility  (0) 2017.10.04