[2019.02.28]

원문 : 애플 개발 문서 Swift 5.0 Language Guide - Strings and Characters

문자열과 문자(Strings and Characters)

문자열(string)은 "hello, world" 또는 "albatross"와 같이 문자들이 연속(series)되어 있는 것입니다. Swift의 문자열은 String 타입으로 표현됩니다. String의 내용(contents)은 다양한 방법으로 사용될수 있으며, Character값의 집합을 포함하고 있습니다.

Swift의 String과 Character 타입은 코드에 있는 텍스트를 유니코드 호환되도록 빠르게 처리합니다. 문자열 생성과 조작(manipulation) 대한 문법은 가볍고 읽을수 있으며, 문자열 리터럴(literal) 문법은 C와 유사합니다. 문자열 연결(concatenation)은 두개의 문자열을 +연산자로 간단하게 결합하고 문자열 가변성(mutability)은 Swift의 다른 값 처럼 상수나 변수중 하나를 골라서 관리됩니다. 또한 상수, 변수, 리터럴, 문자열 표현식을 삽입하하기 위해 문자열을 사용할 수 있으며, 문자열 끼워넣기(string interpolation)에서 처리합니다. 이렇게 하면 보여지고, 저장하며, 추력하는 사용자정의 문자열 값을 쉽게 만들수 있습니다.

구문이 단순함에도 불구하고(despite), Swift의 String타입은 빠르고, 현대적인(modern) 문자열 구현입니다. 문자열은 언제나 독립적인 유니코드 문자로 구성되고, 다양한 유니코드 표현하는 문자들을 지원합니다.

주의
Swift의 String 타입은 Foundation의 NSString클래스와 연결되어(bridged) 있습니다. 또한 Foundation은 NSString에 의해 정듸된 메소드를 사용하기위해 String을 확장합니다. 이는 import Foundation을 하는 경우에, String에서 NSString 메소드를 형변환(casting) 없이 사용할수 있는 것을 의미합니다.
Foundation과 Cocoa의 String 사용데 대한 자세한 것은, Bridging Between String and NSString을 보세요.

문자열 리터럴(String Literals)

문자열 리터럴(string literals)로 미리 정의된 String값을 포함할 수 있습니다. 문자열 리터럴은 따옴표(")로 감싸진 문자들(sequence of characters)입니다.

상수나 변수에 대한 초기값으로 문자열 리터럴을 사용합니다.

let someString = "Some string literal value"

문자열 리터럴 값으로 초기화 되었기 때문에,someString 상수에 대해 Swift가 String의 타입으로 추론하는 것을 주의합니다.

여러줄 문자열 리터럴(Multiline String Literals)

여러줄이 연결된 문자열이 필요한 경우에, 여러줄 문자열 리터럴(multiline string literal)을 사용합니다 - 3개의 쌍따옴표(""")로 감싸는 문자들

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.
"""

소스코드에서 여러줄 문자열 리터럴안에 줄바꿈(line break)이 포함되었을때, 줄바꿈은 문자열의 값에서 나타납니다. 줄바꿈을 사용해서 소크코드를 읽기 쉽게 만들길 원하지만, 줄바꿈이 문자열 값의 일부가 되는것을 원치않는경우, 이러한 줄의 끝부분에 백슬래쉬(\)를 작성합니다.

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."
"""

행문자(line feed)로 시작하거나 끝나는 여러줄 문자열 리터럴을 만들기 위해, 비어 있는 줄을 첫번째 또는 마지막줄에 사용합니다. 예를들어:

let lineBreaks = """

This string starts with a line break.
It also ends with a line break.

"""

여러줄의 문자열은 주변 코드와 일치하도록 들여쓰기(indented) 할 수 있습니다. 닫는 따옴표 앞의 공백(whitespace)은 Swfit가 공백(whitespace)을 다른 줄보다 먼저 무시하는 것을 말합니다. 하지만, 닫는 따옴표 앞에 추가된 줄에서 시작하는 공백(whitespace)을 작성하는 경우, 그 공백(whitespace)는 포함됩니다.

위 예제에서, 전체 여러줄 문자열 리터럴이 들여쓰기(indented)되어 있더라도, 문자열에 있는 첫번째와 마지막 줄은 공백(whitespace)로 시작하지 않습니다. 중간에 있는 줄은 닫는 따옴표보다 들여쓰기가 더 되어 있으므로, 추가적으로 4칸 들여쓰기로 시작합니다.

문자열 리터럴에서의 특수문자(Special Characters in String Literals)

문자열 리터럴은 다음과 같은 특수 문자들을 포함할 수 있습니다.

  • 탈출(escaped) 특수 문자 \0(null 문자: null character), \\(백슬러쉬: backslash), \t(수평 탭: horizontal tab), \n(개행: line feed), \r(복귀: carriage return), \"(쌍 따옴표: double quotation mark), \'(작은 따옴표: single quotation mark)
  • \u{n}으로 작성하는 임의의(arbitrary) 유니코드 스칼라, n은 유요한 유니코드 포인트와 동일한 1-8자리 16진수 숫자(유니코드는 아래에 있는 Unicode에서 논의됩니다.)

아래 코드는 특수 문자들의 4가지 예를 보여줍니다. wiseWords 상수는 두개의 탈출 쌍따옴표(\")를 포함하고 있습니다. dollarSign, blackHeart, sparklingHeart상수는 유니코드 스칼라 포멧으로 보여줍니다.

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

여러줄 문자열 리터럴은 1개가 아닌 3개의 쌍따옴표를 사용하기 때문에, 쌍따옴표(")를 탈출(escaping)문자 없이, 여러줄 문자열 안에 포함할수 있습니다. 여러줄 문자열에 """ 텍스트를 추가하기 위해, 적어도 하나의 따옴표에 탈출문자를 붙여야 합니다. 예를들어:

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

빈 문자열로 초기화하기(Initializing an Empty String)

긴 문자열을 만들기 위한 시작점으로, 빈 String 값을 만들려면, 빈 변수에 문자열 리터럴을 할당하거나 초기화 문법으로 새로운 String 인스턴스로 초기화해서 할당해야 합니다.

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

Boolean isEmpty 프로퍼티를 확인해서 String 값이 비어있는지를 알아봅니다.

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

주의
이 접근법(approach)은 Objective-C와 Cocoa에서의 문자열 변경(mutation)과는 다르며, 문자열을 수정할수 있는지를 나타내기 위해 두 클래스들(NSString과 NSMutableString) 중 하나를 골라야 합니다.

문자열은 값 타입(Strings Are Value Types)

Swift의 String 타입은 값 타입(value type)입니다. 새로운 String값을 만드는 경우에, String값은 함수나 메소드에 전달되거나 상수나 변수에 할당될때 복사됩니다(copied). 각각의 경우, 기존 String 값의 새로운 복사본이 만들어지고, 원래버젼이 아닌 새로운 복사본은 전달되거나 할당됩니다. 값 타입은 Structures and Enumerations Are Value Types에 설명되어 있습니다.

Swift의 기본적으로 복사하는(copy-by-default) String 동작을 String 값을 함수나 메소드에 전달할때, 어디에서 가져왔는지 상관없이, String 값을 정확히 소유하고 있는 것이 명확하다는 것을 보장합니다. 문자열을 직접 수정하지 않으면 전달된 문자열이 수정되지 않는다는 것을 신뢰할 수 있습니다.

화면 뒤에서, Swift의 컴파일러는 꼭 필요할때에만 실제 복사하도록 문자열 사용을 최적화 합니다. 이는 문자열 값으로 작업할때 항상 훌륭한 성능을 발휘하는 것을 의미합니다.

문자들로 작업하기(Working with Characters)

for-in 반복문으로 문자열을 반복해서 String에 대한 개개의 Character 값들을 사용할 수 있습니다.

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

for-in 반복문은 For-in Loops에 설명되어 있습니다.

대신에, Character 타입 주석(annotation을 제공해서 단일 문자 문자열 리터럴로, 독립적인 Character 상수나 변수를 만들수 있습니다.

let exclamationMark: Character = "!"

String 값은 초기화에 인자(argument)로 Character 값들의 배열을 전달해서 구성될 수 있습니다.

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

문자열과 문자 연결하기(Concatenating String and Characters)

String 값은 새로운 String 값을 만들기 위해 더하기 연산자(+)와 함께 추가(또는 연결)될수 있습니다.

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

또한 기존 String 변수에 더하기 할당 연산자(+=)로 String 값을 추가할 수 있습니다.

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

Character값을 String 타입의 append() 메소드를 사용해서 String 변수에 추가할 수 있습니다.

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

주의
Character 값은 단일 문자(character)만을 포함하기 때문에, String이나 Character을 기존 Character변수에 추가할 수는 없습니다.

긴 문자열 줄을 만들기 위해 여러줄 문자열 리터럴을 사용하는 경우, 마지막 줄을 포함해서, 문자열의 모든 줄을 줄바꿈(line break)으로 끝내야 합니다. 예를 들어:

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

위 코드에서, badStart와 end를 연결하면 두 줄의 문자열이 만들어지며, 이는 원하는 결과가 아닙니다. badStart의 마지막줄이 줄바꿈(line break)으로 끝나지 않았기 때문에, 그 줄은 end의 첫번째 줄과 연결됩니다. 이와 반대로, goodStart의 두줄은 줄바꿈으로 끝나므로, end와 연결될때 예상했던것처럼 3줄로 됩니다.

문자열 끼워넣기(String Interpolation)

문자열 끼워넣기(String interpolation)은 상수, 변수, 리터럴과 문자열 리터럴 안쪽의 값들을 포함한 표현을 혼합(mix)해서 새로운 String을 만드는 방법입니다. 한줄과 여러줄 리터럴 모두에서 문자열 끼워넣기를 사용할 수 있습니다. 문자열 리터럴에 삽입하는 각 항목은 한쌍의 괄호로 감싸지며, 백슬래쉬(\)가 앞에 붙습니다.

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

위 예제에서, multiplier의 값은 \(multiplier)로 문자열리터럴에 삽입됩니다. 이 자리표시자(placeholder)는 실제 문자열을 만들기 위해 문자열 끼워넣기를 사용할때 실제 multiplier의 값으로 교체됩니다.

또한 multipler의 값은 나중에 문자열의 긴 표현식의 일부가 됩니다. 이 표현식은 Double(multiplier) * 2.5의 값을 계산하고 문자열에 결과(7.5)를 삽입합니다. 이 경우에, 표현식은 문자열 리터럴 안쪽으로 포함될때 `(Double(multiplier) * 2.5)로 작성됩니다.

주의
끼워넣기된 문자열 안에서 괄호안에 작성한 표현식에는 탈출되지않은(unescaped) 백슬러쉬(\), 복귀(carriage return) 또는 줄바꿈(line feed)을 포함할 수 없습니다. 하지만, 다른 문자열 리터럴은 포함할 수 있습니다.

유니코드(Unicode)

유니코드(Unicode)는 다른 문자 시스템에서 텍스트를 인코딩하고, 표현하고 처리하는 국제 표준입니다. 유니코드는 표준화된 형식의 모든 언어에서 어떤 문자라도 표현이 가능하고, 텍스트 파일이나 웹 페이지 같은 외부 소스로부터 이러한 문자들을 읽고 쓸수 있습니다. 이번 섹션(section)에서 설명된 것처럼, Swift의 String과 Character 타입은 유니코드를 완벽히 호환합니다.

유니코드 스칼라(Unicode Scalars)

화면 뒤에서, Swift의 순수한 String타입은 유니코드 스칼라 값(Unicode scalar values)으로 만들어집니다. 유니코드 스칼라 값은 라틴어 A의 소문자 (LATIN SMALL LATTER A)("a")에 대한 U+0061 또는 아기 병아리 얼굴(FRONT-FACING BABY CHICK)("🐥")에 대한 U+1F425처럼, 문자(character)나 수식어(modifier)에 대한 고유한 21비트 숫자입니다.

모든 21비트 유니코드 스칼라 값이 문자에 할당되지 않는다는 것을 주의합니다. - 일부 스칼라는 나중에 할당하거나 UTF-16 인코딩에 사용하기 위해 예약되어 있습니다. 일반적으로 문자에 할당된 스칼라 값은 위 예제에 있는 LATIN SMALL LETTER A와 FRONT-FACING BABY CHICK 처럼 이름을 가지고 있습니다.

확장된 문자소 클러스터(Extended Grapheme Clusters)

Swift의 Character 타입의 모든 인스턴스는 하나의 확장된 문자소 클러스터(extended grapheme clusters)로 표현됩니다. 확장된 문자소 클러스터는 하나 이상의 유니코드 스칼라의 시퀀스(결합되었을때)로 사람이 읽을 수 있는 문자를 만듭니다.

예제 하나가 있습니다. é라는 글자는 하나의 유니코드 스칼라로 é (LATIN SMALL LETTER E WITH ACUTE, 또는 U+00E9로 표현될 수 있습니다. 하지만, 스칼라 한 쌍(pair)으로 같은 글자를 표현할 수 있습니다 - 표준 문자 e 라틴 소문자 E(LATIN SMALL LETTER E, 또는 U+0065)뾰족한 악센트 결합(COMBINING ACUTE ACCENT) 스칼라(U+0301)이 옵니다. 뽀족한 악센트 결합(COMBINING ACUTE ACCENT) 스칼라는 앞에 오는 스칼라에 그림(graphically)으로 적용되며, 유니코드를 인식하는(Unicode-award) 텍스트 그리는(text-rendering) 시스템에 의해 그려질때 e를 é로 변환합니다.

두 경우 모두, 글자 é는 확장된 문자소 클러스터를 표현하는 하나의 Swift Character 값으로 표현됩니다. 첫 번째 경우에, 클러스터는 하나의 스칼라를 포함합니다; 두 번째 경우에, 두 개의 스칼라로된 클러스터 입니다.

let eAcute: Character = "\u{E9}"                         // é
let combinedEAcute: Character = "\u{65}\u{301}"          // e followed by ́
// eAcute is é, combinedEAcute is é

확장된 문자소 클러스터는 많은 복잡한 스크립트 문자들을 하나의 Character 값으로 표현하는 유연한(flexible) 방법입니다. 예를들어, 한글 알파벳으로부터 한글 음절(syllables)은 구성되거나(precomposed) 분해되는(decomposed) 시퀀스로 표현될수 있습니다. 이러한 표현 모두 Swift에서 하나의 Character 값으로 표현할 자격(qualify)이 됩니다.

let precomposed: Character = "\u{D55C}"                  // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"   // ᄒ, ᅡ, ᆫ
// precomposed is 한, decomposed is 한

확장된 문자소 클러스터는 하나의 Character 값의 일부로 다른 유니코드 스칼라를 감싸기(encolose) 위해 감싸는 표시(COMBINING ENCLOSING CIRCLE 또는 U+20DD) 에 대한 스칼라를 사용가능합니다.

let enclosedEAcute: Character = "\u{E9}\u{20DD}" 
// enclosedEAcute is é⃝

지역을 나타내는 기호에 대한 유니코드 스칼라는 지역 표시하는 상징 글자(REGIONAL INDICATOR SYMBOL LETTER) U(U+1F1FA)와 지역 표시하는 상징 글자(REGIONAL INDICATOR SYMBOL LETTER) S(U+1F1FA)를 하나의 Character값으로 만들기 위해 쌍으로 결합될 수 있습니다.

let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS is 🇺🇸

문자 갯수 세기(Counting Characters)

문자열에서 Character 값들의 갯수를 세기위해, 문자열의 count 프로퍼티를 사용합니다.

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

Swift의 Character 값에 대한 확장된 문자소 클러스터 사용은 문자열 연결하기와 수정하기가 항상 문자열의 문자 갯수에 영향을 주지는 않을 수 있다는 것을 의미합니다.

예를 들어, 4개의 문자 단어 cafe로 새로운 문자열을 초기화하는 경우에, 뽀족한 악센트 결합(COMBINING ACUTE ACCENT) (U+0301)을 문자열의 끝에 추가하면, 최종 문자열은 4개의 문자를 가지고 있을 것이며, 4번째 문자는 e가 아니라 가 됩니다.

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"

주의
확장된 문자소 클러스터는 여러개의 유니코드 스칼라로 구성될 수 있습니다. 이는 다른 문자들이-그리고 같은 문자의 다른 표현-메모리에 저장하는 양이 다를 수 있는 것을 의미합니다. 이 때문에, Swift는 각 문자열의 표현에서 메모리의 양이 같지 않습니다. 결과적으로, 확장된 문자소 클러스터 영역을 결정하기 위해 문자열 반복하지 않고 문자열에 있는 문자들의 갯수를 계산할 수 없습니다. 특히 긴 문자열 값으로 작업하는 경우, count 프로퍼티는 해당 문자열의 문자들을 결정하기 위해서, 반드시 전체 문자열에 있는 유니코드 스칼라를 반복해야 합니다.

count 프로퍼티에 의해 반환된 문자들의 갯수는 동일한 문자들을 포함하는 NSString의 length프로퍼티와 항상 같지 않습니다. NSString의 length는 문자열의 UTF-16을 나타내는 16비트 코드 단위 숫자를 기반으로 하고 문자열에 있는 유니코드의 확장된 문자소 클러스터의 갯수가 아닙니다.

문자열 사용과 수정하기(Accessing and Modifying a String)

메소드와 프로퍼티 또는 서브스크립트 문법을 통해서 문자열을 사용하고 수정합니다.

문자열 인덱스(String Indices)

각 String 값은 연관된 인덱스 타입(index type) String.index를 가지며, 문자열에 있는 각 Character의 위치에 해당(corresponds)합니다.

위에서 언급한 것처럼, 다른 문자들은 저장하는 메모리 양이 다를 수 있으며, 어떤 Character가 특정 위치에 있는지 결정하기 위해, String의 시작이나 끝으로 각 유니코드 스칼라를 반복해야 합니다. 이 때문에, Swift 문자열은 정수형 값으로 인덱스될 수 없습니다.

String의 첫번째 Character의 위치를 접근하기 위해서 startIndex 프로퍼티를 사용합니다. endIndex 프로퍼티는 String에 있는 마지막 문자 뒤의 위치입니다. 결과적으로, endIndex 프로퍼티는 문자열의 서브스크립트에 유효한 인자가 아닙니다. String이 비어있는 경우, startIndex와 endIndex는 같습니다.

주어진 인덱스의 앞과 뒤에 접근하는 것은 String의 index(before:)와 index(after:) 메소드를 사용합니다. 주어진 인덱스로 부터 떨어진 인덱스를 접근하기 위해서, 이런 메소드를 여러번 호출하는 대신에 index(_:offsetBy:) 메소드를 사용할 수 있습니다.

특정 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

문자열에 있는 각 문자들의 모든 인덱스에 접근하기 위해 indices프로퍼티를 사용합니다.

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

주의
Collection 프로토콜을 준수하는 모든 타입에서 startIndex와 endIndex프로퍼티와 index(before:), index(after:), index(_:offsetBy:)메소드를 사용할 수 있습니다. Array, Dictionary, Set과 같은 컬렉션 타입뿐만아니라 여기에 보여지는 것처럼, String도 포함됩니다.

삽입과 제거하기(Inserting and Removing)

문자열의 특정 인덱스에 하나의 문자를 삽입하기 위해, insex(_: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:) 메소드를 사용하고, 특정 범위의 부분 문자열(substrings)을 제거하기 위해, 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"

주의
RangeReplaceableCollection 프로토콜을 준수하는 모든 타입에서 insert(_:at:), insert(contentsOf:at:), remove(at:), removeSubrange(_:)메소드를 사용할 수 있습니다. Array, Dictionary, Set과 같은 컬렉션 타입뿐만아니라 여기에 보여지는 것처럼, String도 포함됩니다.

부분 문자열(Substrings)

문자열에서 부분 문자열(substring)을 얻고자 할때 - 예를 들어, 서브스크립트 또는 prefix(_:) 같은 메소드를 사용 - 다른 문자열이 아니라 SubString의 인스턴스가 됩니다. Swift에서의 부분 문자열은 대부분 문자열과 같은 메소드를을 가지고 있으며, 문자열을 작업하는 같은 방법으로 부분 문자열을 작업할 수 있다는 것을 의미합니다. 결과를 오랫동안 저장해야 할때, 부분 문자열을 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)

문자열처럼, 각 부분 문자열은 부분 문자열을 구성하는 문자들이 저장되는메모리 영역(region)을 가지고 있습니다. 문자열과 부분 문자열간에 다른점은 성능 최적화이며, 부분 문자열은 원본 문자열에 저장하는데 사용되는 메모리의 일부를 재사용할 수 있습니다. (문자열은 비슷한 최적화를 하지만, 두 문자열을 공유하는 메모리의 경우에는 같습니다) 이러한 성능 최적화는 문자열이나 부분 문자열이 수정될때까지 메모리에 복사하는 성능 비용을 지불하지 않는다는 것을 의미합니다. 위에서 언급했던 것처럼, 부분 문자열은 오랜시간 동안 저장하는데 어울리지 않습니다 - 원본 문자열의 저장소를 재사용하기 때문이며, 전체 원본 문자열은 부분 문자열이 사용되는한 반드시 메모리에 유지되어야 합니다.

위 예제에서, greeting은 문자열이며, 문자열을 만드는 문자들이 저장되는 메모리 영역을 가지고 있다는 것을 의미합니다. beginning은 greeting의 부분 문자열이기 때문에, greeting이 사용하는 메모리를 재사용합니다. 이와 반대로, newString은 문자열입니다 - 부분 문자열로부터 생성되었으며, 자신만의 저장소를 가지고 있습니다. 아래 그림은 이러한 관계를 보여줍니다.

주의
String과 Substring 모두 StringProtocol 프로토콜을 준수하며, 문자열 조작함수가 StringProtocol값을 받아들이면 편리하다는 것을 의미입니다. String이나 Substring값으로 함수를 호출 할 수 있습니다.

문자열 비교하기(Comparing Strings)

Swift는 텍스트 값을 비교하는 3가지 방법을 제공합니다; 문자열과 문자 같은지, 접두사(prefix)가 같은지, 접미사(suffix)가 같은지.

문자열과 문자가 같음(String and Character Equality)

문자열과 문자가 같은지는 같음 연산자(==)와 같지 않음 연산자(!=)로 확인되며, Comparison Operators에 설명되어 있습니다.

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")
}
// Prints "These two strings are considered equal"

두개의 String값(또는 두개의 Character 값)은 확장된 문자소 클러스터가 규칙이 같은(canonically equivalent) 경우에 같다고 간주됩니다. 확장된 문자소 클러스터는 언어적인 의미와 모양이 같은 경우에 규칙이 같으며, 심지어는 다른 유니코드 스칼라로 구성된 경우에도 마찬가지 입니다.

예를 들어, 라틴어 소문자 E와 뾰족함(LATIN SMALL LETTER E WITH ACUTE) (U+00E9)은 라틴 소문자 E(LATIN SMALL LETTER) (U+0065) 뒤에 뽀족한 악센트 결합(COMBINING ACUTE ACCENT) (U+0301) 오는것과 규칙이 같습니다. 이러한 확장된 문자소 클러스터 모두 문자 é를 표현하는데 유효한 방법이고, 그래서 귝칙이 같은 것으로 간주됩니다.

// "Voulez-vous un café?" using LATIN SMALL LETTER E WITH ACUTE
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"

// "Voulez-vous un café?" using LATIN SMALL LETTER E and COMBINING ACUTE ACCENT
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"

if eAcuteQuestion == combinedEAcuteQuestion {
    print("These two strings are considered equal")
}
// Prints "These two strings are considered equal"

반대로(Conversely), 영어로서 사용되는 라틴 대문자 A(LATIN CAPITAL LETTER A) (U+0041 또는 "A")는 러시아에서 사용되는 `키릴 대문자 A(CYRILLIC CAPITAL LETTER A) (U+0410 또는 A)는 같지 않습니다. 그 문자들은 비슷하게 보이지만, 언어적인(linguistic) 의미가 같지 않습니다.

let latinCapitalLetterA: Character = "\u{41}"

let cyrillicCapitalLetterA: Character = "\u{0410}"

if latinCapitalLetterA != cyrillicCapitalLetterA {
    print("These two characters are not equivalent.")
}
// Prints "These two characters are not equivalent."

주의
Swift에서 문자열과 문자 비교는 지역(locale)과는 상관없습니다.

접두사와 접미사 같음(Prefix and Suffix Equality)

문자열이 특정 문자열 접두사나 접미사를 가지고 있는지 검사하기 위해서, 문자열의 hasPrefix(_:)와 hasSuffix(_:) 메소드를 호출하며, 둘다 String타입의 인자 하나를 받고 Boolean 값을 반호나합니다.

아래 예제는 세익스피어(Shakespeare)의 로미오와 줄리엣(Romeo and Juliet)의 처음 2장의 장면 위치를 나타내는 문자열 배열입니다.

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 mansion",
    "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"
]

Act 1의 장면 수를 계산하기 위해서 romeoAndJuliet배열에서 hasPrefix(_:) 메소드를 사용할 수 있습니다.

var act1SceneCount = 0
for scene in romeoAndJuliet {
    if scene.hasPrefix("Act 1 ") {
        act1SceneCount += 1
    }
}
print("There are \(act1SceneCount) scenes in Act 1")
// Prints "There are 5 scenes in Act 1"

비슷하게, 캐플릿(Capulet)의 저택과 수사 로렌스(Friar Lawrence)의 방 주변의 장소를 가진 장면의 갯수를 세기 위해 hasSuffix(_:) 메소드를 사용합니다.

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")
// Prints "6 mansion scenes; 2 cell scenes"

주의
hasPrefix(_:)와 hasSuffix(_:) 메소드는 각 문자열에 있는 확장된 문자소 클러스터간에 문자 대 문자(character-by-character)로 규칙이 같은지 비교하기 위해 실행하며, String and Character Equality에 설명되어 있습니다.

문자열의 유니코드 표현(Unicode Representations of Strings)

유니코드 문자열이 텍스트 파일이나 다른 저장소로 쓰여질때, 문자열에 있는 유니코드 스칼라는 몇가지 유니코드 인코딩 형식(encoding forms)으로 인코딩 됩니다. 각 형식(form)은 문자열을 코드 단위(code units)라는 작은 덩어리(chunks)로 인코딩 합니다. UTF-8 인코딩 형식(8비트 코드 단위로 문자열을 인코딩), UTF-16 인코딩 형식(16비트 코드 단위로 문자열을 인코딩), UTF-32 인코딩 형식(32비트 코드 단위로 문자열을 인코딩)을 포함합니다.

Swift는 문자열의 유니코드를 표현하는데 사용하는 몇가지 다른 방법을 제공합니다. 유니코드 확장된 문자소 클러스터로 각 Character 값을 사용하기 위해 for-in구분으로 문자열을 반복 할 수 있습니다. 이 과정은 Working with Characters에 설명되어 있습니다.

또는, 3가지 다른 유니코드 호환(Unicode-compliant) 표현법 중의 하나로 String 값을 사용합니다.

  • UTF-8 코드 단위의 컬렉션(문자열의 utf8프로퍼티로 사용됨)
  • UTF-16 코드 단위의 컬렉션(문자열의 utf16프로퍼티로 사용됨)
  • 21비트 유니코드 스칼라 값의 컬렉션, 문자열의 UTF-32 인코딩 형식과 동일(문자열의 unicodeScalars 프로퍼티로 사용됨)

아래의 각 예제는 다음 문자열의 다른 표현을 보여주며, D, o, g, !! 느낌표 두개 표시(DOUBLE EXCLAMATION MARK) 문자들 또는 유니코드 스칼라(U+203C)로 구성되고, 🐶문자(개의 얼굴(DOG FACE) 또는 유니코드 스칼라 U+1F436)으로 구성됩니다.

let dogString = "Dog‼🐶"

UTF-8 표현(UTF-8 Representation)

utf8 프로퍼티를 반복해서 String의 UTF-8 표현을 사용할 수 있습니다. 이 프로퍼티는 String.UTF8View의 타입이며, 부호가 없는 8비트(UInt8) 값의 컬렉션이며, 문자열의 UTF-8 표현에서 각 바이트당 하나를 차지합니다.

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

위 예제에서, 첫번째 3개의 10진수 codeUnit 값 (68, 111, 103)은 D, o, g 문자를 표현하며, UTF-8 표현은 ASCII 표현과 같습니다. 다음 3개의 10진수 codeUnit 값 (226, 128, 188)은 느낌표 두개(DOUBLE EXCLAMATION MARK)문자의 3바이트 UTF-8표현입니다. 마지막 4개의 codeUnit 값 (240, 159, 144, 182)은 개 얼굴(DOG FACE)문자의 4바이트 UTF-8 표현입니다.

UTF-16 표현(UTF-16 Representation)

utf16 프로퍼티를 반복해서 String의 UTF-16 표현을 사용할 수 있습니다. 이 프로퍼티는 String.UTF16View 타입이며, 부호가 없는 16비트(UInt16) 값의 컬렉션이며, 문자열의 UTF-16 표현에서 각 16비트 코드당 하나를 차지합니다.

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

다시한번, 처음 3개의 codeUnit값 (68, 111, 103)은 D, o, g 문자를 표현하며, UTF-16 코드 단위는 문자열의 UTF-8 표현에서와 같은 값을 가집니다(유니코드 스칼라는 ASCII 문자를 표현하기 때문).

4번째 codeUnit 값 (8252)는 느낌표 두개(DOUBLE EXCLAMATION MARK)문자에 대해 유니코드 스칼라 U+203C를 표현하는 16진수 203C 값과 같은 10진수입니다. 이 문자는 UTF-16에서 하나의 코드단위로 표현될 수 있습니다.

5번째와 6번째 codeUnit 값(55357과 56374)은 개의 얼굴(DOG FACE) 문자를 표현하는 UTF-16 쌍으로 대응하는 표현(surrogate pair) 입니다. 이 값은 이 값은 U+D83D(10진수 55357 값)의 높은 대응(high-surrogate) 값이고 U+DC36(10진수 56374 값)의 낮은 대응(low-surrogate) 값 입니다.

유니코드 스칼라 표현(Unicode Scalar Representation)

unicodeScalars 프로퍼티를 반복해서 String 값의 유니코드 스칼라 표현을 사용할 수 있습니다. 이 프로퍼티는 UnicodeScalarView 타입이며, UnicodeScalar 타입의 값의 컬렉션입니다.

각 UnicodeScalar은 스칼라의 21비트값을 반환하는 value프로퍼티를 가지며, Uint32 값으로 표현됩니다.

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

첫번째 3개의 UnicodeScalar 값 (68, 111, 103)에 대한 vlaue 프로퍼티는 다시한번 D, o, g 문자를 표현합니다.

4번째 codeUnit 값 (8252)는 또다시 느낌표 두개(DOUBLE EXCLAMATION MARK)문자에 대해 유니코드 스칼라 U+203C를 표현하는 16진수 203C값과 같은 10진수입니다.

5번째. value프로퍼티와 마지막UnicodeScalar128054는 16진수 1F436과 같은 10진수이며, 개의 얼굴(DOG FACE)문자 에 대한 유니코드 스칼라 U+1F436을 나타냅니다.

value 프로퍼티 값을 조회하는 대신에, 각 UnicodeScalar 값은 문자열 끼워넣기(interpolation) 처럼, 새로운 String값을 구성하는데 사용될 수 있습니다.

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


'Swift > Language Guide' 카테고리의 다른 글

컬렉션 타입(Collection Types)  (0) 2019.03.05
문자열과 문자(Strings and Characters)  (0) 2019.02.28
기본 연산자(Basic Operators)  (0) 2019.02.26
기초(The Basics)  (0) 2019.02.12
Advanced Operators  (0) 2018.09.18
Access Control  (0) 2018.09.18
Posted by 까칠코더