아요 개발 일기

[Swift] Strings and Characters (문자열과 문자) 본문

Swift/The Swift Programming language

[Swift] Strings and Characters (문자열과 문자)

김망복 2022. 12. 30. 11:49
반응형

안녕하세요 ;-)

오늘은 Strings and Characters에 대해 알아보도록 하겠습니다!

이 글은 Swift 공식문서 The Swift 5.5 Language Guide - StringsAndCharacters 을 참고하여 작성하였습니다.


String(문자열)은 "hello, world" 또는 "albatross"와 같이 문자들이 연속되어 있는 것 입니다.

Swift의 문자열은 String 타입으로 표현됩니다. String의 내용은 다양한 방법으로 사용될 수 있으며, character값의 집합을 포함하고 있습니다.

Swift의 String과 Character 타입은 유니코드를 준수하는 코드 텍스트 작업 방법을 빠르게 제공합니다.

 

구문이 단순함에도 불구하고, Swift의 String 타입은 빠르다, 현대적인 문자열 구현체이다.

모든 문자열은 구성되어있다 인코딩된 독립적인 유니코드 문자로,

그리고 문자들이 여러가지 유니코드로 표현되는 문자를 엑세스 할 수 있도록 지원합니다.

 

Swift의 문자열 유형은 Foundation의 NSString 클래스와 연결(bridged)되어있습니다.
또한 Foundation은 NSString에 의해 정의된 메서드를 사용하기위해 String을 확장합니다. 
즉, import Foundation을 하는 경우에, String에서 NSString 메소드를 (casting) 없이 사용할 수 있는 것을 의미합니다. 
Foundation과 Cocoa의 String 사용에 대한 자세한 내용은 Bridging Between String and NSString을 참조하십시요.

 


String Literals (문자열 리터럴)

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

 

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

 

let someString = "Some string literal value"

Swift는 문자열 리터럴 값으로 초기화 되었기 때문에, 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.
"""

 

소스 코드가 다중 행 문자열 리터럴의 내부에 줄 바꿈을 포함하면 해단 줄 바꿈은 문자열 값에도 나타납니다.

줄 바꿈을 사용하여 소스 코드를 읽기 쉽지만 줄 바꿈을 문자열 값에 포함시키지 않으려면 줄 끝에 \ 를 작성합니다.

 

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)은 Swift에게 다른 모든 행보다 먼저 무시할 공백이 무엇인지 알려줍니다. 

하지만, 닫는 따옴표 앞에 추가된 줄에서 공백을 작성하는 경우, 해당 공백은 포함됩니다. 

 

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

 

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

문자열 리터럴에는 다음과 같은 특수 문자가 포함될 수 있습니다.

 

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

아래 코드는 이러한 특수 문자의 네 가지 예를 보여줍니다. wiseWords 상수에는 두 개의 탈출 쌍따옴표(\")가 있습니다.

dollarSign, blackHeart, and 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개 쌍따옴표를 사용하므로, 쌍따옴표(")를 탈출 문자 없이, 여러줄 문자열 안에 포함할 수 있습니다. 여러줄 문자열에 """ 텍스트를 추가하려면 적어도 따옴표 중 하나 이상에 탈출문자를 붙여야합니다.

 

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

[출력 화면]
This line doesn't begin with whitespace.
Escaping the first quotation mark """
Escaping all three quotation marks """

 

Extended String Delimiters ( 확장 된 문자열 구분 기호)

확장 구분 기호를 사용하면 특수 문자를 문자열에 포함시킬 수 있습니다.

따옴표(") 내에 문자열을 배치하고 숫자 기호(#)로 묶습니다.

let ExtendedString1 = #"Line 1\nLine 2"#
let ExtendedString2 = #"Line 1\nLine 2"#
let ExtendedString3 = #"Line 1\#nLine 2"#
let ExtendedString4 = ###"Line1\###nLine2"###

print("test1:", ExtendedString1)
print("test2:", ExtendedString2)
print("test3:", ExtendedString3)
print("test4:", ExtendedString4)


[출력 결과]
test1: Line 1\nLine 2
test2: Line 1\nLine 2
test3: Line 1
Line 2
test4: Line1
Line2

확장된 구분 기호를 사용하여 작성된 문자열 리터럴도 다중 행 문자열 리터럴이 될 수 있습니다. 확장 구분 기호를 사용하여 """ 텍스트를 다중 행 문자열에 포함시켜 리터럴을 종료하는 기본 동작을 재정의할 수 있습니다. 

 

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

[출력 결과]
Here are three more double quotes: """

 


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

 

빈 문자열 값을 더 긴 문자열을 빌드하기 위한 시작점으로 만들려면,

변수에 빈 문자열 리터럴을 할당하거나 초기화(initializing) 구문으로 새 문자열 인스턴스를 초기화해서 할당해야합니다.

 

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
이 접근 방식은 Objective - C 및 Coaco의 문자열 돌연변이와는 다릅니다. 여기서 두 클래스(NSString 및 NSMutableString) 중 하나를 선택하여 문자열의 변이가 가능한지 여부를 나타냅니다.

String Are Value Types ( 값타입 문자열)

 

Swift의 문자열 유형은 값 타입(value type)입니다.

새 String 값을 생성하는 경우, 해당 String 값은 함수 또는 메서드에 전달 되거나 상수 또는 변수에 할당될 때 복사됩니다.

각 경우 기존 String 값의 새로운 복사본이 만들어지고 원본 버전이 아닌 새로운 복사본은 전달되거나 할당됩니다.

값 타입은  Structures and Enumerations Are Value Types 에 설명되어 있습니다. 

 

Swift의 copy-by-default String 동작은

함수 또는 메서드가 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 타입 주석을 제공하여 단일 문자열 리터럴에서 독립적인 실행형 문자 상수 또는 변수를 생성할 수 있습니다.

let exclamationMark: Character = "!"

String 값은 character 값의 배열을 초기화의 인자로 전달하여 구성 할 수 있습니다.

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

Concatenating String and Characters (문자열과 문자의 결합)

 

문자열 값은 추가 연산자(+)와 함께 추가(또는 연결)하여 새 문자열 값을 생성할 수 있습니다.

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 타입의 append() 메소드를 사용해서 String 변수에 추가 할 수 있습니다.

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

 

Character 값은 단일 문자(character)만 포함해야 하므로 기존 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와 연결하면 두 줄의 문자열이 생성되는데, 이는 원하는 결과가 아닙니다.

badStart의 마지막 라인은 줄 바꿈으로 끝나지 않기 때문에 해당 라인은 첫 번째 줄과 결합됩니다.

이와는 반대로, goodStart의 두 줄은 모두 줄 바꿈으로 끝나므로, 끝과 결합하면 결과는 예상했던 것처럼 세 개의 줄을 가집니다.


String Interpolation ( 문자열 삽입)

문자열 삽입(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의 값으로 대체됩니다.

 

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."
삽입 문자열 내의 괄호 안에 쓰는 식에는 탈출되지 않은 (unescaped) 백슬래시(\), 복귀 (carriage return) 또는 줄바끔 (line feed)을 포함할 수 없습니다. 하지만, 다른 문자열 리터럴은 포함할 수 있습니다.

Unicode (유니코드)


유니코드(Unicode)는 다른 문자 시스템에서 텍스트를 인코딩, 표현 및 처리하는 국제 표준입니다.

유니코드는 표준화된 형식의 모든 언어에서 어떤 문자라도 표현이 가능하고, 텍스트 파일이나 웹 페이지 같은 외부 소스에서 이러한 문자를 읽고 쓸 수 있습니다. Swift의 문자열 및 문자 유형은 이번 섹션에서 설명한 것처럼 완전히 유니코드를 준수합니다.

 

Unicode Scalar Values ( 유니코드 스칼라 값)

화면 뒤에서, Swift의 기본 String 유형이 유니코드 스칼라 값으로 작성됩니다. 유니코드 스칼라 값(Unicode scalar value)은 

LATIN SMART LETTER A("a")의 경우 U+0061 또는 FRONT-FACH BABY CHICK("🐥")의 경우 U+1F425와 같이 문자(character)나 수식어(modifier)에 대한 고유한 21비트 숫자 입니다.

 

21비트 유니코드 스칼라 값이 모두 문자에 할당되는 것은 아니며, 일부 스칼라 값은 나중에 할당하거나 UTF-16 인코딩에 사용하기 위해 예약되어 있습니다.일반적으로 문자에 할당된 스칼라 값도 위의 예에서 LATIN SMART LETTER A 및 FRONT-FACH BICK와 같은 이름을 가집니다.

 

 

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

문자소? 의미를 나타내는 최소 문자 단위

Swift의 문자 유형의 모든 인스턴스는 단일 확장 그래프 클러스터를 나타냅니다. 확장된 문자 클러스터(Extended Grapheme Clusters)는 하나 이상의 유니코드 스칼라 시퀀스로 , 사람이 읽을 수 있는 단일 문자를 생성합니다.

 

여기 예시가 있습니다. 문자 é는 단일 유니코드 스칼라로(LATIN SMART LETTER E WITH ACUTE, 또는 U+00E9)로 표현될 수 있습니다.  하지만, 스칼라 한 쌍으로 같은 글자를 표현할 수 있습니다 - 표준 문자 e (LATIN SMART LETTER E, 또는 U+0065), 그 뒤에

급성 악센트 결합 (COMBINING ACUTE ACCENT) 스칼라(U+0301)가 옵니다. 

급성 악센트 결합 (COMBINING ACUTE ACCENT) 스칼라는 앞에 오는 스칼라에 그래픽(graphically)으로 적용되며,

유니코드를 인식하는(Unicode-award) 텍스트 렌더링(text-rendering) 시스템에 의해 렌더링될 때 e를 é로 변환합니다.

 

두 경우 모두 문자 é는 확장된 문자소 클러스터를 나타내는 단일 Swift Character value로 표시됩니다.

첫 번째 경우에는 클러스터에 하나의 스칼라가 포함되고 두 번째 경우에는 두 개의 스칼라가 포함된 클라스터입니다.

 

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

 

확장된 문자소 클러스터는 여러 복잡한 스크립트 문자를 단일 문자 값으로 유연하게 나타낼 수 있습니다. 예를 들어, 한글의 한글 음절은  한글 알파벳으로부터 한글 음절(syllables)은 구성되거나(precomposed) 분해되는(decomposed) 시퀀스로 표현될수 있습니다. 이러한 표현은 모두 스위프트에서 단일 Character value으로 한정됩니다.

 

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

 

확장된 문자소 클러스터를 사용하면 스칼라가 하나의 문자 값의 일부로 다른 유니코드 스칼라를 둘러싸는 표시 (COMBINING ENCLOSING CIRCLE, or 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 값은 연관된 인덱스 타입인 String.index를 가지며, 문자열에 있는 각 Character의 위치에 해당합니다.

위에서 언급한 것처럼, 다른 문자들은 메모리에 저장하는 양이 다를 수 있으며, 어떤 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

 

문자열 범위를 벗어난 인덱스 또는 문자열 범위 밖의 인덱스에 있는 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:)메소드를 사용할 수 있습니다.
Array, Dictionary, Set과 같은 컬렉션 타입뿐만아니라 여기에 보여지는 것처럼, String도 포함됩니다.

 

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:) 메소드를 사용하고, 특정 범위의 부분 문자열(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 (부분 문자열)


문자열에서 부분 문자열을 얻고자할때 - 예를 들어, 서브스크립트 또는 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)

 

문자열처럼, 각각 부분 문자열은 부분 문자열을 구성하는 문자들이 저장되는메모리 영역을 가지고 있습니다.

문자열과 부분 문자열간에 다른점은 성능 최적화이며, 부분 문자열은 원본 문자열에 저장하는데 사용되는 메모리의 일부를 재사용할 수 있습니다. (문자열은 비슷한 최적화를 하지만, 두 문자열을 공유하는 메모리의 경우에는 같습니다)

이러한 성능 최적화는 문자열이나 부분 문자열 각각 수정될때까지 메모리에 복사하는 성능 비용을 지불하지 않는다는 것을 의미합니다.

위에서 말한 바와 같이, 부분 문자열은 오랜시간 동안 저장하는데 적절하지 않습니다 - 원본 문자열의 저장소를 재사용하기 때문이며, 전체 원본 문자열은 부분 문자열이 사용되는한 반드시 메모리에 유지되어야 합니다.

 

위 예제에서, greeting은 문자열이며, 문자열을 만드는 문자들이 저장되는 메모리 영역을 가지고 있다는 것을 의미합니다. 

beginning은 greeting의 부분 문자열이기 때문에, greeting이 사용하는 메모리를 재사용합니다.

이와 반대로, newString은 문자열입니다 - 부분 문자열로부터 생성되었으며, 자신만의 저장소를 가지고 있습니다. 아래 그림은 이러한 관계를 보여줍니다.

 

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


문자열 비교하기(Comparing Strings)


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

 

문자열과 문자가 같음(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"

 

 

두개의 Strin g값(또는 두개의 Character 값)은 확장된 문자소 클러스터가 규칙이 같은 경우에 같다고 간주됩니다.

확장된 문자소 클러스터는 언어적인 의미와 모양이 같은 경우에 규칙이 같으며, 심지어는 다른 유니코드 스칼라로 구성된 경우에도 마찬가지 입니다.

 

예를 들어, 라틴어 소문자 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"

 

반대로, 영어로서 사용되는 라틴 대문자 A (U+0041 또는 "A")는 러시아에서 사용되는 키릴 대문자 A(U+0410 또는 A)는 같지 않습니다.

그 문자들은 비슷하게 보이지만, 언어적인 의미가 같지 않습니다.:

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

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

if latinCapitalLetterA != cyrillicCapitalLetterA {
    print("These two characters aren't equivalent.")
}
// Prints "These two characters aren't equivalent."

 

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

 

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"

 

비슷하게, 캐플릿의 저택과 수사 로렌스(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 (문자열의 유니코드 표현)


유니코드 문자열이 텍스트 파일이나 다른 저장소로 쓰여질때, 문자열에 있는 유니코드 스칼라는 몇가지 유니코드 인코딩 형식으로 인코딩 됩니다. 각 형식은 문자열을 코드 단위라는 작은 덩어리로 인코딩 합니다. 

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, !! 느낌표 두개 표시문자들 또는 유니코드 스칼라(U+203C)로 구성되고, 🐶문자(개의 얼굴(DOG FACE) 또는 유니코드 스칼라 U+1F436)으로 구성됩니다.

 

let dogString = "Dog‼🐶"

 

UTF-8 Representation (UTF-8 표현)

utf8 프로퍼티를 반복해서 String의 UTF-8 표현을 사용할 수 있습니다.

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

 

위 예제에서, 첫번째 3개의 10진수 codeUnit 값 (68, 111, 103)은 D, o, 그리고 g 문자를 표현하며, UTF-8 표현은 ASCII 표현과 같습니다. 다음 3개의 10진수 codeUnit 값 (226, 128, 188)은 느낌표 두개문자의 3바이트 UTF-8표현입니다.

마지막 4개의 codeUnit 값 (240, 159, 144, 182)은 🐶 개 얼굴 문자의 4바이트 UTF-8 표현입니다.

 

UTF-16 Representation (UTF-16 표현)

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)는 느낌표 두개문자에 대해 유니코드 스칼라 U+203C를 표현하는 16진수 203C 값과 같은 10진수입니다.

이 문자는 UTF-16에서 하나의 코드단위로 표현될 수 있습니다.

5번째와 6번째 codeUnit 값(55357과 56374)은 개의 얼굴 문자를 표현하는 UTF-16 쌍으로 대응하는 표현입니다.

이 값은 이 값은 U+D83D(10진수 55357 값)의 높은 대응 값이고 U+DC36(10진수 56374 값)의 낮은 대응 값 입니다.

 

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)는 또다시 느낌표 두개 문자에 대해 유니코드 스칼라 U+203C를 표현하는 16진수 203C값과 같은 10진수입니다.

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

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

 

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

 

반응형