안녕하세요. 개발하는 정주입니다.
오늘은 문자열과 문자 (Strings and Characters)를 정리해보겠습니다.
* 공식 문서 내용을 한 줄 한 줄 읽는 것에 의의를 두었습니다.
* 파파고의 힘을 빌려 번역했으며 잘못된 번역이 있다면 댓글로 알려주세요.
새로 배운 점
- Swift의 String과 Character 타입은 유니코드 호환으로 텍스트 처리를 빠르게 하였다는 점
- Multiline String 관련 문법
- String의 isEmpty가 Property라는 점
- Extended Grapheme Clusters의 문법
- 여러 개의 문자를 합쳐 하나의 그래픽으로 나타낸다는 점
- 똑같이 보이는 문자라도 다른 양의 메모리가 필요할 수 있다는 점
- SubString은 문자열을 참조하는 구조라는 점
서론
문자열은 "hello, world" 또는 "albatross"같은 연속된 문자이며 String 타입으로 표현됩니다.
String 값은 Character 값의 컬렉션을 포함하여 여러 가지 방법으로 접근할 수 있습니다.
Swift의 String과 Character 타입은 유니코드 호환 방법을 제공하여 코드의 텍스트 처리가 빠릅니다.
문자열 생성과 사용 구문은 C와 유사한 문자열 문법과 같이 가볍고 읽기 쉽습니다.
문자열 연결은 두 문자열 사이에 더하기 연산자를 넣어 연결이 가능하고 상수 또는 변수로 관리 가능합니다.
또한 string interpolation이라는 프로세스에서 문자열을 사용하여 상수, 변수, 리터럴 및 표현식을 긴 문자열에 삽입할 수 있습니다.
이것은 화면에 표시, 저장, 출력을 위한 커스텀 문자열 값을 쉽게 생성할 수 있습니다.
간단한 구문임에도 Swift의 String 타입은 빠르고, 현대식의 문자열입니다.
모든 문자열은 인코딩에 독립적인 유니코드 문자로 구성되어 있으며 다양한 유니코드 표현의 문자에 접근할 수 있도록 지원합니다.
Swift의 String 타입은 Foundation의 NSString 클래스와 연결되어 있습니다.
Foundation은 String에서 NSString에 의해 정의된 메서드를 사용할 수 있도록 String을 확장합니다.
덕분에 Foundation을 import 하면 캐스팅 없이 String에서 NSString 메서드를 접근할 수 있습니다.
문자열 리터럴 (String Literals)
코드 안에 미리 정의된 String 값을 문자열 리터럴 (string literals)로
문자열 리터럴은 큰따옴표 (")로 둘러쌓인 문자의 연속입니다.
상수 또는 변수의 초기값으로 문자열 리터럴을 사용합니다.
let someString = "Some string literal value"
Swift는 문자열 리터럴 값으로 초기화되었기 때문에 someString 상수를 String 타입으로 유추합니다.
여러 줄 문자열 리터럴 (Multiline String Literals)
여러 줄의 문자열이 필요하면 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.
"""
코드에서 여러 줄 문자열 리터럴에 줄 바꿈을 포함하면 줄바꿈도 문자열의 값으로 나타납니다.
가독성을 위해 줄바꿈을 하고 싶지만 실제로 줄 바꿈이 되지 않기를 원할 때는 라인 끝에 백 슬래시(\)를\ 쓰면 됩니다.
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."
"""
여러 줄 문자열 리터럴에 시작 또는 끝에 빈줄을 추가하고 싶다면 정의문 처음 또는 마지막 줄에 빈 줄을 추가하면 됩니다.
let lineBreaks = """
This string starts with a line break.
It also ends with a line break.
"""
여러 줄문자열은 닫는 따옴표 (""") 앞의 공백만큼 다른 모든 줄의 공백을 무시합니다.
위의 예제에서 전체가 들여 쓰기 되어있지만 첫 번째와 마지막 줄은 앞에 공백이 없습니다.
중간 줄은 닫는 따옴표보다 들여 쓰기가 더 많으므로 4칸 들여 쓰기로 시작합니다.
문자열 리터럴에 특수 문자 (Special Characters in String Literals)
문자열 리터럴은 아래와 같은 특수 문자를 포함할 수 있습니다:
- 이스케이프 된 문자 \0 (null 문자), \\ (백 슬래시), \t (수평 탭), \n (개행), \r (캐리지 리턴), \" (큰따옴표)와 \' (홑 따옴표)
- \u {n}로 쓰인 임의의 유니코드 스칼라 값. 여기서 n 은 1-8 자리의 16진수
아래 코드는 특수 문자의 4개의 예를 보여줍니다. wiseWords 상수는 2개의 이스케이프 된 큰따옴표를 포함합니다.
dollarSign, blackHeart 그리고 sparklingHeartsparklingHeart는 유니코드 스칼라 형태를 보여줍니다.
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
여러 줄 문자열 리터럴은 쌍따옴표 3개를 사용하기 때문에 이스케이프 없이 여러줄 문자열 리터럴 안에 큰따옴표 (")를 포함할 수 있습니다. 여러 줄 문자열 텍스트에 """"""를 포함하려면 따옴표에 적어도 하나의 이스케이프를 포함해야 합니다.
let threeDoubleQuotationMarks = """
Escaping the first quotation mark \"""
Escaping all three quotation marks \"\"\"
"""
확장된 문자열 구분 기호 (Extended String Delimiters)
문자열에 특수 문자를 보여주기 위해서는 확장된 구분 기호(extended delimiters) 안에 문자열 리터럴을 넣으면 됩니다.
문자열을 따옴표 (")로 둘러싸고 # 기호로 둘러쌉니다. 예를 들어 문자열 리터럴 #"Line 1\nLine 2"# 을 출력하면 2줄로 출력되지 않고 개행 문자 (\n)가 출력됩니다.
문자열 리터럴에서 특수문자 사용이 필요하다면 이스케이프 문자 (\) 다음에 선언된 #의 숫자만큼 # 기호를 포함하면 됩니다.
예를 들어 문자열 #"Line 1\nLine 2"# 에 개행을 수행하고 싶다면 #"Line 1\#nLine 2"#로 작성하면 되고 ###"Line1\###nLine2"### 도 개행이 됩니다.
확장된 구분 기호를 사용하여 생성된 문자열 리터럴은 여러 줄 문자열 리터럴로 사용할 수도 있습니다.
여러 줄문자열에 텍스트 """를 포함하기 위해 리터럴을 종료하는 기본 동작을 재정의 하여 확장된 구분기호를 사용할 수 있습니다.
let threeMoreDoubleQuotationMarks = #"""
Here are three more double quotes: """
"""#
빈 문자열 초기화 (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
String 값은 isEmpty 프로퍼티로 비어있는지 체크할 수 있습니다.
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 type)
새로운 String 값을 생성한다면 String 값은 함수 또는 메서드에 전달될 때나 상수 또는 변수에 대입될 때
각 경우에 존재하는 String 값의 새로운 복사본이 생성되고 원본이 아닌 새로운 복사본이 전달되거나 할당됩니다.
Swift의 기본 String으로의 복사는 함수 또는 메서드가 String 값을 전달할 때 출처에 관계없이 String 값은 정확하다고 보장합니다. 전달된 문자열은 직접 수정하지 않는한 수정되지 않습니다.
Swift의 컴파일러는 꼭 필요할 때 실제 복사가 이뤄지도록 문자열 사용을 최적화합니다.
이것은 문자열을 값 유형으로 사용할 때는 항상 뛰어난 성능을 얻을 수 있다는 의미입니다.
문자 작업 (Working with Characters)
문자열과 for-in 루프로 String 의 각각의 Character 값에 접근할 수 있습니다.
for character in "Dog!🐶" {
print(character)
}
// D
// o
// g
// !
// 🐶
하나의 문자 문자열 리터럴을 Character 타입을 명시하여 단독의 Character 상수 또는 변수를 생성할 수 있습니다:
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"
또한 존재하는 String 변수에 덧셈 대입 연산자 (+=)로 String 값을 연결할 수 있습니다.
var instruction = "look over"
instruction += string2
// instruction now equals "look over there"
String 타입의 append() 메서드를 이용하여 String 변수에 Character 값을 추가할 수 있습니다.
let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// welcome now equals "hello there!"
Character 값은 반드시 하나의 문자만 포함해야 하므로 String 또는 Character 를 추가할 수 없습니다.
여러 줄문자열 리터럴을 이용하여 긴 문자열의 라인을 구성하는 경우 문자열 매 라인 끝에 공백이 포함포함돼야합니다.
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 를 출력하면 원하는 결과와 다르게 2줄 문자열로 출력됩니다.
badStart의마지막 줄에 공백이 포함되지 않았기 때문에 end 의 첫번째 줄과 결합된 결과를 얻게 됩니다.
반면에 goodStart 마지막 줄에 개행이 포함되므로 end 와 결합이 되면 3줄로 출력됩니다.
문자열 삽입 (String Interpolation)
문자열 삽입 (String interpolation) 은 상수, 변수, 리터럴, 그리고 문자열 리터럴에 값이 포함된 표현식을 혼합해 새로운 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) 로 문자열 리터럴 안에 삽입되며 실제 문자열이 생성될 때 실제 값으로 대체됩니다.
multiplier의값은 문자열에 표현식의 일부이기도 합니다.
이 표현식은 Double(multiplier) * 2.5 의 값을 계산하고 문자열에 결과 (7.5) 를 삽입합니다.
이 경우 표현식은 문자열 리터럴에 포함될 때 \(Double(multiplier) * 2.5) 로 작성합니다.
확장된 문자열 구분기호를 사용하여 문자열 삽입으로 사용할 문자를 포함하는 문자열을 생성할 수 있습니다.
print(#"Write an interpolated string in Swift using \(multiplier)."#)
// Prints "Write an interpolated string in Swift using \(multiplier)."
확장된 구분기호를 사용하는 문자열에서 문자열 삽입을 사용하기 위해 문자열의 시작과 끝에 #의 갯수만큼 백슬래시 다음에 #을 넣어주면 됩니다.
print(#"6 times 7 is \#(6 * 7)."#)
// Prints "6 times 7 is 42."
소괄호 안에 작성한 표현식에 삽입된 문자열은 백슬래시 (\), 캐리지 리턴, 또는 개행을 포함할 수 없습니다.
그러나 다른 문자열 리터럴은 포함할 수 있습니다.
유니코드 (Unicode)
유니코드 (Unicode) 는 인코딩, 표기, 그리고 다른 쓰기 시스템에서의 텍스트 프로세싱을 위한 국제 표준입니다.
거의 모든 언어의 문자를 표준화 된 형식으로 표현하고 텍스트 파일 또는 웹 페이지와 같은 외부 소스에서 해당 문자를 읽고 쓸 수 있습니다.
Swift의 Character 타입은 유니코드를 완벽하게 지원합니다.
유니코드 스칼라 값 (Unicode Scalar Values)
Swift의 기본 String 타입은 유니코드 스칼라 값 (Unicode scalar values) 으로부터 생성됩니다.
유니코드 스칼라 값은 유니크 한 21-bit 숫자 또는 수식어입니다.
예시로는 LATIN SMALL LETTER A ("a")의 경우 U+0061이고 FRONT-FACING BABY CHICK ("🐥")의 경우 U+1F425입니다.
모든 21-bit 유니코드 스칼라 값이 한 문자에 할당되는 것은 아닙니다.
어떤 스칼라는 나중에 할당되거나 UFT-16 인코딩에 사용하기 위해 지정되어 있습니다.
문자에 할당된 스칼라 값은 일반적으로 위의 예에서 LATIN SMALL LETTER AA와FRONT-FACING BABY CHICK 같이 이름을 가지고 있습니다.
확장된 문자소 클러스터 (Extended Grapheme Clusters)
Swift의 Character 타입의 모든 인스턴스는 하나의 확장된 문자소 클러스터 (extended grapheme cluster)cluster)로 표기됩니다.
하나의 확장된 문자소 클러스터는 결합되었을 때 사람이 읽을 수 있는 하나의 문자인 하나 이상의 유니코드 스칼라 입니다.
문자 é 은 단일 유니코드 스칼라 é (LATIN SMALL LETTER E WITH ACUTE, or U+00E9)로 표기 가능합니다.
그러나 동일 문자를 표준 문자 e (LATIN SMALL LETTER E, or U+0065) 뒤에 COMBINING ACUTE ACCENT 스칼라 (U+0301)가 오는 스칼라 쌍으로 표시될 수도 있습니다.
COMBINING ACUTE ACCENT 스칼라는 유니코드 인식 텍스트 렌더링 시스템에서 렌더링 될 때 e를 é로변환합니다.
두 경우 모두 문자 é 는 확장된 문자소 클라터를 나타내는 단일 Swift 문자값으로 표시됩니다.
첫 번째의경우에는 클러스터에는 단일 스칼라가 포함됩니다. 두번째의 경우에는 두 스칼라의 클러스터 입니다.
let eAcute: Character = "\u{E9}" // é
let combinedEAcute: Character = "\u{65}\u{301}" // e followed by ́
// eAcute is é, combinedEAcute is é
확장된 문자소 클러스터는 많은 복잡한 스크립트 문자를 하나의 Character 값으로 표기하는 방법입니다.
예를 들어 한글의 한글 음절은 단일 시퀀스 또는 분해된 시퀀스로 표시될 수 있습니다.
이 두 표현은 모두 Swift에서 단일 Character 값으로 표시됩니다.
let precomposed: Character = "\u{D55C}" // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
// precomposed is 한, decomposed is 한
확장된 문자소 클러스터는 마크 (예를 들어 COMBINING ENCLOSING CIRCLE 또는 U+20DD)를 둘러싸는 스칼라를 사용하여
다른 유니코드 스칼라를 단일 문자값의 일부로 묶을 수 있습니다.
let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute is é⃝
국가 표시 기호에 대한 유니코드 스칼라를 쌍으로 결합하여 단일 Character 값으로 만들 수 있습니다.
예를 들어 REGIONAL INDICATOR SYMBOL LETTER UU(U+1 F1 FA)와 REGIONAL INDICATOR SYMBOL LETTER S (U+1F1F8) 의 결합이 있습니다.
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 값을 위한 확장된 문자소 클러스터 사용은 문자열의 문자 count에 영향을 주지 않습니다.
예를 들어 4개의 문자 단어인 cafe로새로운 문자열을 초기화 하고 문자열 끝에 COMBINING ACUTE ACCENT(U+0301)을 추가하면
최종 문자열은 4번째 문자가 e 가 아닌 é 가 되지만 여전히 문자 카운트는 4입니다.
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"
확장된 문자소 클러스터로 인해 똑같이 보이는 문자라도 저장할 땐 서로 다른 양의 메모리가 필요할 수 있습니다.
따라서 확장된 문자소 클러스터의 길이를 결정하기 위해서는 문자열을 반복하지 않고는 문자열의 길이를 계산할 수 없습니다.
특히 긴 문자열 값으로 작업하는 경우, 해당 문자열의 문자를 확인하려면 전체 문자열의 유니코드 스칼라에 대해 count 속성을 반복해야 합니다.
count 속성에서 반환되는 문자 수가 동일한 문자를 포함하는 NSString의 길이 속성과 항상 같지는 않습니다.
NSString의 길이는 문자열의 UTF-16 표현 내에 있는 16비트 코드 단위의 수를 기반으로 하며 유니코드 확장된 문자소 클러스터 수를 기반으로 하지 않습니다.
문자열 접근과 수정(Accessing and Modifying a String)
메서드와 프로퍼티 또는 서브 스크립트(subscript) 문법으로 문자열에 접근, 수정할 수 있습니다.
문자열 인덱스(String Indices)
각 String 값은 문자열에 각 Character의 위치에 해당하는 String.Index인 인덱스 타입(index type)을 가지고 있습니다.
문자마다 저장할 메모리 양이 다를 수 있으므로 특정 위치에 있는 Character를 확인하려면 해당 String의 시작이나 끝에서 유니코드 스칼라를 반복해야 합니다. 따라서 Swift 문자열은 정수값으로 index를 생성할 수 없습니다.
String에 첫 번째 Character에 접근하기 위해 startIndex 프로퍼티를 사용합니다.
endIndex 프로퍼티는 String에 마지막 문자의 다음 위치이므로 문자열의 서브 스크립트에 유효한 인자가 아닙니다.
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
문자열 범위에 벗어나는 인덱스로 접근하거나 문자열 범위에 벗어나는 인덱스의 Character를접근하려고 하면 런타임 에러가 발생합니다.
greeting[greeting.endIndex] // Error
greeting.index(after: greeting.endIndex) // Error
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:) 메서드를 사용할 수 있습니다.
이것은 여기서 봤듯이 String 뿐만 아니라 Array, Dictionary, 그리고 Set과같은 Collection 타입도 포함됩니다.
삽입과 삭제(Inserting and Removing)
문자열의 특정 인덱스에 문자 하나를 삽입하려면 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"
RangeReplaceableCollection 프로토콜을 준수하는 어떠한 타입에서든 insert(_:at:), insert(contentsOf:at:), remove(at:), removeSubrange(_:) 메서드를 사용할 수 있습니다.
이것은 String 뿐만 아니라 Array, Dictionary, 그리고 Set과같은 Collection 타입도 포함됩니다.
부분 문자열 (Substrings)
문자열에서 부분문자를 얻기 위해 prefix (_:)와 같은 서브스크립트 메소드를 이용할 수 있는데, 그렇게 얻은 부분 문자열은 문자열(String) 인스턴스가 아니라 부분문자열(SubString) 인스턴스입니다.
만약 부분 문자열을 단기간에 사용하는게 아니라 오랜기간 사용한다면 문자열 인스턴스로 바꿔서 사용하는게 좋습니다.
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)
문자열과 마찬가지로 각 부분 문자열에는 부분 문자열을 구성하는 문자가 저장되는 메모리 영역이 있습니다.
문자열과 부분 문자열의 차이점은 성능 최적화입니다.
부분 문자열은 원본 문자열을 참조하여 사용합니다.(문자열은 비슷한 최적화를 갖지만 메모리를 공유하는 두 문자열은 서로 같습니다.)
이 성능 최적화는 문자열이나 부분 문자열을 수정할 때까지 메모리 복사 비용을 추가로 지불할 필요가 없다는 것을 의미합니다.
위에서 언급했듯이, 부분 문자열은 장기 저장에 적합하지 않습니다.
원본 문자열의 메모리를 참조하기 때문에, 전체 원본 문자열은 부분 문자열이 사용되는 한 메모리에 보관해야 합니다.
위의 예에서, greeting은 String이며, 이는 String을 구성하는 문자가 저장되는 메모리 영역을 가지고 있음을 의미합니다.
beginning 은 greeting의부분 문자열이기 때문에 greeting 이 사용하는 메모리를 재사용합니다.
반대로 newString 은 문자열입니다. 즉 이것은 부분 문자열에서 생성될 때 자신만의 저장소를 가집니다. 아래는 이러한 관계를 보여줍니다.
String과 Substring 은 모두 StringProtocol을준수합니다. 따라서 둘 모두 문자열 조작 함수를 호출할 수 있습니다.
문자열 비교(Comparing Strings)
Swift는 문자열과 character equality, prefix equality, suffix equality과 같이
텍스트 값을 비교하는 3가지 방법이 있습니다.
문자열과 문자 비교(String and Character 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")
}
// Prints "These two strings are considered equal"
유니코드는 결합된 문자열로 비교를 합니다.
예를 들어 LATIN SMALL LETTER E WITH ACUTE (U+00E9)은 LATIN SMALL LETTER E (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"
반대로 영어로 사용된 LATIN CAPITAL LETTER A (U+0041, or "A")와 러시아어로 사용된 CYRILLIC CAPITAL LETTER A (U+0410, or "А")은 같지 않습니다. 이 문자는 모양은 같지만 언어적 의미가 같지 않습니다.
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의 문자열과 문자 비교는 로케일을 구분하지 않습니다.
접두사와 접미사 동등성 (Prefix and Suffix Equality)
문자열이 특정 접두사나 접미사를 가지고 있는지 확인하려면 문자열의 hasPrefix(_:)와 hasSuffix(_:) 메서드를 호출하면 됩니다.
두 메서드는 하나의 String 타입 인자를 받고 부울 값을 반환합니다.
아래 예에서는 셰익스피어의 로미오와 줄리엣 에 처음 두막의 장면을 나타내는 문자열 배열을 나타냅니다.
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"
]
hasPrefix(_:) 메서드를 이용하여 romeoAndJuliet 배열의 연극 Act 1의 장면의 갯수를 셀 수 있습니다.
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"
hasSuffix(_:) 메서드를 사용하여 Capulet’s mansion 과 Friar Lawrence’s cell 의 장면의 갯수를 구할 수 있습니다.
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(_:) 메서드는 각 문자열에 확장된 문자소 클러스터 간의 문자 단위로 동등한지 수행합니다.
문자열의 유니코드 표현 (Unicode Representations of Strings)
유니코드 문자가 텍스트 파일이나 다른 저장소에 쓰여질 때 유니코드 스칼라는 UTF-8, UTF-16, UTF-32 등 다양한 유니코드 인코딩 방식이 사용됩니다.
세 개의 다른 유니코드 호환 표현 중 하나로 String 값에 접근할 수 있습니다.
- UTF-8 코드 유닛(문자열의 utf8 프로퍼티로 접근)
- UTF-16 코드 유닛(문자열의 utf16 프로퍼티로 접근)
- UTF-32 인코딩 형식에 해당하는 21-bit 유니코드 스칼라 값(문자열의 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 표현에 접근할 수 있습니다.
이것은 문자열의 UTF-8 표현에서 각 바이트에 대해 하나씩 부호가 없는 8-bit (UInt8) 값의 모음인 String.UTF8View 타입입니다.
for codeUnit in dogString.utf8 {
print("\(codeUnit) ", terminator: "")
}
print("")
// Prints "68 111 103 226 128 188 240 159 144 182 "
위 예제에서 첫번째 3개의 codeUnit 값 (68, 111, 103)은 ASCII와 같은 UTF-8 표현인 문자 D, o, 그리고 g를나타냅니다.
다음 3개의 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 표현에 접근할 수 있습니다.
이 프로퍼티는 문자열의 UTF-16 표현에서 각 16-bit 코드 유닛에 대해 하나씩 부호가 없는 16-bit (UInt16) 값의 모음인 String.UTF16View 타입입니다.
for codeUnit in dogString.utf16 {
print("\(codeUnit) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 55357 56374 "
유니코드 스칼라 표현 (Unicode Scalar Representation)
unicodeScalars 프로퍼티 반복을 통해 String 값에 유니코드 스칼라 표현에 접근할 수 있습니다.
이 프로퍼티는 UnicodeScalar 타입의 값의 모음인 UnicodeScalarView 타입입니다.
각 UnicodeScalar 는 UInt32 값 안에 표현된 스칼라의 21-bit 값은 반환하는 value 프로퍼티를 가지고 있습니다:
for scalar in dogString.unicodeScalars {
print("\(scalar.value) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 128054 "
for scalar in dogString.unicodeScalars {
print("\(scalar) ")
}
// D
// o
// g
// ‼
// 🐶
참조
https://docs.swift.org/swift-book/
https://bbiguduk.gitbook.io/swift/