본문 바로가기

Swift/Swift Language

Swift 언어 가이드 - 문자열과 문자

 

문자열과 문자

문자열 (string)은 여러 문자(characters)가 모인 것입니다. 예를 들어, "hello, world" 혹은, "albatross"와 같은 것이 문자열입니다. 스위프트의 문자열은 String 타입으로 표현합니다. String에 접근하는 방법은 여러가지가 있습니다. 주로 Character의 컬렉션 형태로 String에 접근합니다.

스위프트의 StringCharacter 타입은 유니코드 형식을 따릅니다. 문자열 생성과 조작 문법은 가볍고 읽기 편합니다. 문자열을 연결(String concatenation)하는 방법은 매우 간단합니다. 두 개의 String+ 연산자로 이어 붙입니다. 그리고 상수와 변수 중 하나를 선택하면 문자열이 변경될 수 있는지 불변으로 둘지를 결정할 수 있습니다. 길이가 긴 문자열에 표현식이나 상수, 변수, 리터럴을 삽입하여 사용할 수 있습니다. 이것을 문자열 삽입(string interpolation)이라고 합니다. 문자열 삽입을 수정한 문자열을 저장하고 출력할 때 유용하게 이용할 수 있습니다.

문법의 간결함에도 불구하고, 스위프트의 String 타입은 빠르고 현대적인 문자열 기능을 가집니다. 모든 문자열은 인코딩에 독립적인 유니코드 문자열로 이루어져 있습니다. 또한, 문자열을 다양한 유니코드 형태로 접근할 수 있습니다.

참고

스위프트의 String 타입은 Foundation의 NSString 클래스와 연관되어 있습니다. Foundation 패키지는 String을 확장하여 NSString으로 정의된 메소드를 expose할 수 있습니다. 즉, Foundation 패키지를 추가하여 String 타입을 캐스팅하지 않고 NSString메소드에 접근할 수 있습니다.

Foundation과 Cocoa의 String 에 대한 자세한 사항은 String과 NSString 연결하기 (Bridging Between String and NSString)을 참고하세요.

 

문자열 리터럴

여러분의 코드안의 String 값을 문자열 리터럴로 포함할 수 있습니다. 문자열 리터럴은 쌍따옴표(")로 둘러싸인 문자의 연속입니다.

초기값으로 사용할 문자열 리터럴을 상수나 변수에 사용하세요.

let someString = "Some string literal value"

여기서 주의해야할 점은 스위프트는 someString 상수를 String타입으로 추론합니다. 왜냐하면 someString문자열이 문자열 리터럴 값으로 초기화되었기 때문입니다.

다중 행 문자열 리터럴

만약 여러분이 문자열을 여러 행으로 확장하고 싶다면, 다중 행 문자열 리터럴 (multiline string literal)을 사용하세요. 다중행 문자열 리터럴은 3개의 쌍따옴표로 감싸야합니다.

let quotation = """
하얀색 토끼는 구경거리를 늘어 놓았다. 그는 물었다. "어디서 시작하길 원하십니까?,
임금님?"
임금은 엄중하게 말했다. "시작부터 시작하라" 그리고 이어서,
끝이 다가오면 그 때 멈추어라"
"""

다중 행 문자열 리터럴은 따옴표의 시작과 끝 사이에 있는 모든 행을 포함합니다. 첫번째 행의 문자열은 세 개의 쌍따옴표(""")의 시작점부터 시작하며 3개의 쌍따옴표로 종료됩니다.

let singleLineString = "이 둘은 똑같습니다."
let multilineString="""
이 둘은 똑같습니다.
"""

소스코드에 행을 구분하는 개행문자를 포함할 수 있습니다. 이 개행문자 또한 문자열의 값에 그대로 표현됩니다. 만약 소스코드의 가독성을 위해 개행문자를 사용하고 싶지만 문자열의 값에는 포함하고 싶지 않다면 역슬래시(\)를 사용하세요.

let softWrappedQuotation="""
하얀색 토끼는 구경거리를 늘어 놓았다. 그는 물었다. "어디서 시작하길 원하십니까?, \
임금님?"
임금은 엄중하게 말했다. "시작부터 시작하라" 그리고 이어서, \
끝이 다가오면 그 때 멈추어라"
"""

다중 행 문자열을 줄바꿈과 함께 사용하고 싶다면 공백 행을 시작과 끝에 삽입합니다.

let lineBreaks = """

이 문자열은 줄바꿈과 함께 시작되죠.
그리고 줄바꿈으로 끝나죠.

"""

문장을 닫는 세 개의 쌍따옴표(""") 이전의 공백은 스위프트가 어떤 공백을 무시할지를 알려줍니다. 하지만, 공백을 행의 시작에 사용할 때 문장을 닫는 쌍따옴표보다 더 공백을 추가해서 사용한다면, 해당 공백은 적용 됩니다.

../_images/multilineStringWhitespace_2x.png

위의 예제를 보면, 전체 문자열 행들이 들여쓰기 되어 있지만, 첫번째와 마지막 행은 공백으로 시작하지 않습니다. 중간의 행이 문장을 닫는 쌍따옴표보다 더 들여쓰기가 되어있으므로 중간의 행이 4개의 들여쓰기가 적용되는 것입니다.

문자열 리터럴에서 특수 문자를 사용하기

문자열 리터럴은 다음의 특수 문자를 사용할 수 있습니다.

  • 탈출 특수 문자 \0(널 문자), \\(역슬래시), \t수평 탭, \n (줄 바꿈), \r (캐리지 리턴), \"(쌍 따옴표), \'(따옴표)
  • 임의의 유니코드 스칼라값은 \u{n}으로 사용합니다. 여기서 n은 1에서 8까지의 16진수 값입니다.

아래의 코드는 4개의 특수문자를 사용하는 예제입니다. wiseWords 상수는 두 개의 쌍따옴표를 가지고 있습니다. dollarSign, blackHeartsparklingHeart는 유니코드 스칼라 포맷을 가지고 있습니다.

let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imagination is more important than knowledge" - Einstein
let dollarSign = "\u{24}" // $
let blackHeart = "\u{2665}" //  ♥
let sparklingHeart = "\u{1F496}"// 💖

다중 행 문자열 리터럴이 3개의 쌍따옴표를 사용하므로, 다중 행 문자열 리터럴안에서는 쌍따옴표를 이스케이핑 없이 사용할 수 있습니다. 대신, """을 다중 행 문자열에 사용하려면 최소한 하나의 쌍따옴표에 이스케이프 처리를 해야합니다.

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

확장된 문자열 경계기호

확장된 경계기호안에 문자열 리터럴을 삽입하여 특수문자를 그대로 사용할 수 있습니다. 예를 들어, #"Line 1\nLine 2"#이라는 문자열 리터럴을 출력한다면, \n은 다음 줄을 만드는 것이 아니라 문자 그대로 표현됩니다.

만약 문자열 리터럴에 특수 문자를 사용하고 싶다면 이스케이프 문자인 (\)이후에 문자 개수를 맞추어야 합니다. 예를 들어, #"Line 1\nLine 2"#라는 문자열을 사용하면서 \n이 다음 줄을 만들어내도록 할때, #"Line 1\#nLine 2"#을 사용합니다. 이와 비슷하게 ###"Line1\###nLine2"###을 사용하여도 줄 바꿈이 적용됩니다.

확장된 경계기호를 사용하여 생성된 문자열 리터럴은 다중 행 문자열이 될 수 있습니다. 예를 들어, 다음의 코드는 탈출 문자 없이 쌍따옴표 3개를 다중행 문자열 안에서 사용하였습니다.

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

빈 문자열 초기화하기

길이가 긴 문자열을 만들기 위해 개발자들은 종종 아무런 값이 없는 String 값을 만들고 시작합니다. 또는 새로운 String 인스턴스를 생성 메소드를 이용해 초기화합니다.

var emptyString = "" // 빈 문자열 리터럴
var anotherEmptyString = String() // 초기화 문법
// 위의 두 문자열은 모두 비어있다.

String값이 비어있는지 확인하려면 isEmpty 속성을 사용합니다.

if emptyString.isEmpty {
  print("Nothing to see here")
}
// 출력되지 않음

문자열 가변성(Mutability)

특정한 String이 변경될 수 있는지 혹은 상수로 선언하여 불변하도록 할 것인지를 지정할 수 있습니다.

var variableString = "Horse"
variableString += " and carriage"
// variableString 은 "Horse and carriage"

let constantString = "Highlander"
constantString += " and another Highlander"
// 컴파일 타임 에러가 발생함

주의

이러한 접근법은 Objective-C와 Cocoa의 문자 가변성과는 다릅니다. Objective-C나 Cocoa는 NSStringNSMutableString 클래스 둘 중하나를 선택하여 문자열이 변할 수 있는지를 결정합니다.

String은 값 타입(Value Types)이다.

스위프트의 String타입은 값 타입입니다. 만약 여러분이 새로운 String값을 만든다면, 그 String 값은 복사되어 함수나 메소드로 전달됩니다. 새로운 복사본으로 String 값이 만들어지고 그 새로운 복사본이 전달되거나 상수나 변수로 할당됩니다.

스위프트가 String을 복사하는 방식은 함수나 메서드가 String을 전달받을 때 정확한 String 값임을 보장합니다. 따라서 String값이 전달될 때 원본 String 값은 수정되지 않다는 것입니다.

실제로는, 스위프트의 컴파일러가 문자열을 사용하는 것을 최적화하여 절대적으로 필요한 경우만 복제를 합니다. 이 뜻은, 문자열을 값 타입으로 다룰 때 최상의 성능을 얻을 수 있다는 것입니다.

문자 다루기

String 값에 있는 각각의 Character 값을 for-in 반복문으로 순회하여 접근할 수 있습니다.

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

위 예제와 다른 방법으로는, 단일한 Character 상수 혹은 변수를 생성할 수 있습니다.

let exclamationMark: Character = "!"

String 값은 Character 값의 배열로 전달받아 생성될 수도 있습니다.

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"
instruction += string2
// intruction은 "look over there"과 같음

Character 값을 String 변수에 추가하려면 String 타입의 메서드인 append()를 사용하세요.

let exclamtionMark: Character = "!"
welcome.append(exclamationMark)
// welcome은 "hello there!"과 같음

참고

String 혹은 Character 값을 이미 존재하는 Character 변수에 추가할 수 없습니다. 왜냐하면 Character값은 오로지 하나의 문자만 가질 수 있기 때문입니다.

만약 여러분이 다중 행 문자열 리터럴을 사용하여 더 많은 줄의 문자열을 만들고 싶다면, 문자열의 마지막 줄에 줄바꿈을 추가해야합니다.

let badStart = """
one
two
"""
let end = """
three
"""
print(badStart + end)
// 두 줄이 출력됨
// one
// twothree

let goodStart = """
one
two

"""
print(goodStart + end)
// 세 줄이 출력됨
// one
// two
// three

위의 코드에서 badStartend를 연결하면 두 줄의 문자열이 만들어집니다. 원래 의도는 세 줄의 문자열을 원했지만, badStart의 마지막 줄이 줄 바꿈으로 끝나지 않았기 때문입니다. goodStart는 줄 바꿈으로 끝났으므로 end의 문자열이 세 번째 줄에 위치할 수 있는 것입니다.

문자열 삽입(Interpolation)

문자열 삽입은 새로운 String값을 상수, 변수, 리터럴, 표현식을 조합하여 만드는 방법입니다. 여러분은 문자열 삽입을 단일 행이나 다중 행 리터럴 모두에 사용할 수 있습니다. 삽입해야할 문자열 리터럴을 소괄호로 감싸도록 하고 맨 앞에 역슬래시(\)를 붙여줍니다.

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

위의 예제에서 mutliplier의 값이 \(multiplier)를 대신하여 삽입되었습니다. 실제의 값을 생성하기 위해 문자열 삽입이 계산될 때, 이 플레이스 홀더는 mutliplier의 실제 값과 교체 됩니다.

multiplier의 값은 문자열의 더 큰 표현식의 부분입니다. 이 표현식은 Double(multiplier) * 2.5를 계산하고 (7.5) 라는 결과를 문자열에 삽입합니다. 이 경우, 이 표현식이 문자열 리터럴 값에 포함된다면 \(Double(multiplier) * 2.5)와 같이 작성됩니다.

_확장된 문자열 경계기호_를 사용하면 문자열 삽입이 값을 대체하지 않도록 할 수 있습니다.

print(#"스위프트에서 삽입된 문자열을 작성할 때 \(multiplier).을 사용하라"#)
// "스위프트에서 삽입된 문자열을 작성할 때 \(multiplier).을 사용하라"가 그대로 출력됨

문자열 삽입을 확장된 경계기호를 사용하는 문자열 안에 사용하고 싶다면, 역슬래시 앞에 숫자기호(#)의 개수와 문자열 시작과 끝의 숫자기호(#)의 갯수를 일치시킵니다.

print(#"6 곱하기 7 은 \#(6 * 7)."#)
// 6곱하기 7은 42

참고

연결된 문자열 내에서 소괄호에 작성된 표현식은 탈출되지 않은 역슬래시(\)를 사용할 수 없습니다.

유니코드

유니코드는 다른 시스템간의 문자열 인코딩, 표현, 프로세싱을 위한 국제 표준입니다. 여러분은 유니코드를 사용하여 어떤 언어든지 표준화된 형태로 문자열을 표현할 수 있고, 다른 소스들(텍스트 파일, 웹 페이지)의 문자를 읽거나 쓸 수 있습니다. 스위프트의 StringCharacter는 완전하게 유니코드를 따릅니다.

유니코드 스칼라 값 Unicode Scalar Values

스위프트의 String값은 _유니코드 스칼라 값(_Unicode Scalar Values)_으로 만들어집니다. 유니코드의 스칼라 값은 유일한 21비트 개수의 문자(Character)와 수정자(Modifier)로 이루어져 있습니다. 예를 들어 U+0061소문자 A("a")이거나 U+1F425은 앞을 쳐다보는 병아리("🐥")입니다.

주의할 점은 21 비트의 유니코드 스칼라 값은 문자(Character)에만 국한되는 것이 아닙니다. 몇몇의 스칼라 값은 추후의 선언을 위해 남겨두거나 UTF-16 인코딩에서 사용하기 위함입니다. 문자에 할당된 대부분의 스칼라 값은 이름을 가집니다. 앞선 예와 같이 LATIN LETTER AFRONT-FACING BABY CHICK가 그것입니다.

확장된 서기소 군집(Extended Grapheme Cluster)

스위프트 문자 타입의 모든 인스턴스는 단일한 확장된 서기소 군집을 표현합니다. 확장된 서기소 군집은 하나 이상의 유니코드 스칼라 값의 연속입니다.

예를 들어 설명하겠습니다. 문자 é은 단일한 유니코드 스칼라인 é(LATINE SMALL LETTER E WITH ACUTE, 또는 U+00E9)를 뜻합니다. 하지만 똑같은 문자인 e(LATIN SMALL LETTER E, orU+0065)을 뜻합니다.

두 경우 모두 문자 é는 단일한 스위프트 Character 값을 나타냅니다. 첫 번째 예제는, 군집이 단일한 스칼라값을 포함한다는 것입니다. 두 번째 예제는 두 개의 스칼라의 군집입니다.

let eAcute: Character = "\u{E9}" // é
let combineEAcute: Character = "\u{65}\u{301}" // e와  ́가 결합됨 
// 두 경우 모두 é,  eAcute is é, combinedEAcute is é

확장된 서기소 군집은 복잡한 스크립트 문자를 단일한 문자 값으로 표현하는 유연한 방법입니다. 예를 들어 한글 음절을 조합되거나 분해된 순서로 표현할 수 있습니다. 스위프트에서 두 표현 방식은 모두 단일한 문자를 의미합니다.

let precomposed: Character = "\u{D55C}" // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ㅎ, ㅏ, ㄴ
// 조합된 값은 "한", 분해된 값도 "한"

확장된 서기소 군집은 둘러싸인 기호를 유니코드 스칼라값으로 사용할 수 있도록 해줍니다.

let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute는 é⃝ 값이다.

국기를 표현하기 위한 유니코드 스칼라를 하나의 문자로 표현하려면 유니코드를 조합하면 됩니다. 예를 들어, REGIONAL INDICATOR SYMBOL LETTER U(U+1F1FA)REGIONAL INDICATOR SYMBOL LETTER S(U+1F1F8):이 있습니다.

let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUs 는 🇺🇸 와 같다.

문자 개수 세기

문자열에 있는 Character 값의 개수를 가져오려면 count속성을 사용하면 됩니다.

let unusalMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
print("unusaualMenagerie는 \(unusualMenagerie.count) 문자를 갖습니다.")
// "unusualMenagerie는 40 문자를 갖습니다."

스위프트가 문자 (Character) 값에 확장된 서기소 군집을 사용하지만, 문자열 연결이나 수정이 항상 문자 개수에 영향을 미치지는 않습니다.

예를 들어, 새로운 문자열을 cafe라는 4 개의 문자로 초기화하고, COMBINING ACUTE ACCENT(U+0301)을 문자의 마지막에 붙이면 여전히 문자 개수는 4라는 것입니다.

var word = "cafe"
print("\(word)의 문자 수는 \(word.count)입니다.")
// "cafe의 문자 수는 4입니다."

word += "\u{301}" // COMBINING ACUTE ACCENT

print("\(word)의 문자 수는 \(word.count)입니다.")
// "café의 문자 수는 4입니다."

참고

확장된 서기소 군집은 여러 개수의 유니코드 스칼라로 이루어져 있습니다. 이 뜻은 문자는 서로 다른 크기의 메모리를 필요로 한다는 것입니다. 이러한 이유로 인해, 문자열의 문자 개수는 문자를 순회하지 않는 이상 계산할 수 없다는 것입니다. 만약 여러분이 매우 긴 문자열을 다룰 때 count라는 속성이 유니코드 스칼라 값을 순회해야만 한다는 점에 유의해야 합니다.

count 속성에 의해 반환되는 문자의 개수는NSStringlength속성과 항상 일치하지 않습니다. NSString은 UTF-16 표현 방식에서 16비트 개수 유닛을 기반으로 합니다.

문자열 접근 및 수정하기

문자열의 메소드와 속성을 이용하여 문자열을 수정하거나 접근할 수 있습니다. 또는 서브스크립트 문법을 사용할 수도 있습니다.

문자열 인덱스

각각의 String 값은 연관된 인덱스를 갖습니다. String.Index는 각각의 Character의 위치와 대응됩니다.

위에서 언급한대로, 다른 문자들은 각각 다른 크기의 메모리 양을 갖습니다. 따라서, 어떤 Character가 특정한 위치에 있는지를 판단하려면 해당 String의 처음부터 끝까지 있는 유니코드 스칼라를 순회해야 합니다. 이러한 이유로 스위프트는 정수 값으로 문자열을 인덱싱하여 접근할 수 없습니다.

문자열의 첫 번째 문자의 위치에 접근하려면 startIndex 속성을 사용하세요. endIndex 속성은 String의 마지막 문자의 뒷 인덱스 번호와 같습니다. 그렇기 때문에, endIndex는 문자열 서브스크립트에 적합한 인자가 아닙니다. String이 비어 있다면 startIndexendIndex는 서로 같습니다.

주어진 인덱스 값의 앞, 뒤의 인덱스에 접근하려면 index(before:)index(after:) 함수를 사용하세요. 어떤 인덱스에 멀리 떨어진 인덱스에 접근하고 싶다면 index(_:offsetBy:) 메서드를 사용하세요.

서브스크립트 문법을 사용하면 특정한 문자열의 인덱스에 있는 문자에 접근할 수 있습니다.

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(greeintg.startIndex, offsetBy: 7)
greeting[index]
// a

문자열의 범위 밖의 인덱스에 접근하면 런타임 에러가 발생합니다.

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

참고

startIndexendIndex 속성과 index(before:), index(after:) 그리고 index(_offsetBy:) 메소드를 어떠한 타입이든 Collection 프로토콜에 적합한 것이면 사용할 수 있습니다. 이것에는 컬렉션 타입은 Array, Dictionary 그리고 Set이 있습니다.

삽입과 삭제

문자열에 인덱스를 지정하여 문자를 삽입하고 싶다면 어떻게 해야할까요? insert(_:at:) 메서드를 사용하면 됩니다.

var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome은 "hello!"과 같음

welcome.insert(contentsOf: " there", at: welcome.index(before: welcome.endIndex))
// welcome은 "hello there!"과 같음

문자열의 특정한 인덱스에 위치한 문자를 삭제하고 싶을때는 remove(at:) 메서드를 사용합니다. 특정 범위의 문자열 부분을 제거하려면 removeSubrange(_:)를 사용합니다.

welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome은 "hello there"과 같음
let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome은 "hello"와 같음

참고

insert(_:at:), insert(contentsOf:at:), remove(at:) 그리고 removeSubrange(_:) 메서드를 사용할 때 어떠한 타입이든 RangeReplaceableCollection 프로토콜에 적합한 것이면 가능합니다.이 경우는 String이 포함되며 Array, Dictionary, Set이 컬렉션 타입에 해당합니다.

부분 문자열

여러분이 prefix(_:)와 같은 메서드를 사용하거나 서브스크립트를 이용하여 문자열의 부분 문자열을 얻었다면 그것은 또 다른 문자열이아니라 Substring의 인스턴스입니다. 스위프트의 Substring은 문자열의 메서드와 거의 같습니다. 하지만 문자열과 차이가 있는 점은 짧은 시간 동안만 문자를 다룰 수 있다는 것입니다. 따라서 부분 문자열, SubstringString으로 변환하여 좀 더 긴 시간동안 사용하세요. 예를 들어,

let greeting = "Hello, world!"
let index = greeting.firstIndex(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning 은 "Hello"와 같음

// 부분 문자열을 문자로 변환
let newString = String(beginning)

문자열과 같이 부분 문자열은 메모리의 일부분을 차지합니다. 해당 메모리 영역에서 부분 문자열로 이루어진 문자들이 저장됩니다. 부분 문자열과 문자열의 차이는 성능 최적화에 있어서 부분 문자열이 원본 문자열의 메모리의 부분을 재사용 할 수 있다는 것입니다. 즉, 문자열 혹은 문자를 수정하기 전까지는 메모리를 복사해야하는 비용이 없다는 것입니다. 위에서 언급한 것과 같이, 부분 문자열을 긴 시간 동안 저장하는 것은 적합하지 않습니다. 왜냐하면 원본 문자열의 메모리 공간을 재사용하기 때문입니다. 원본 문자열은 부분 문자열이 사용되는 한 계속 메모리에 남습니다.

위의 예제에서 greeting은 문자열입니다. 이것은 메모리의 영역을 차지하고 있습니다. beginninggreeting의 부분 문자열이므로 greeting의 메모리를 재사용합니다. 이와 반대로 newString은 부분 문자열으로부터 새로 만들어진 문자열입니다. 아래의 도식화를 통해 관계를 확인할 수 있습니다.

../_images/stringSubstring_2x.png

참고

StringSubstringStringProtocol 프로토콜에 따릅니다. StringProtocol의 값을 받는 문자열 조작 함수를 간편하게 사용할 수 있습니다. 이러한 함수는 String 혹은 Substring 값을 이용하여 호출할 수 있습니다.

문자열 비교하기

스위프트는 문자열 값을 비교하는 세 가지의 방법을 제공합니다. 문자열과 문자 동등성, 접두사 동등성, 접미사 동등성입니다.

문자열과 문자 동등성

문자열과 문자 동등성은 "일치(equal to)" 연산자 (==)와 그 반대의 "비일치(not equal to)" 연산자(!=) 에 의해 확인됩니다.

let quotation = "We're a lot alike, you and I."
let sameQuotation = "We're a lot alike, you and I."
if quotation == sameQuotation {
  print("이 두 문자는 같다.")
}
// "이 두 문자는 같다."가 출력됨

String 값은 문자열의 확장된 서기소 군집이 표준적으로 동등할 때 간다고 판단됩니다. 확장된 서기소 군집은 두 값이 서로 똑같은 언어적 의미와 모양을 갖추면 표준적으로 일치합니다. 유니코드 스칼라 값이 일치하지 않아도 그렇습니다.

예를 들어, LATIN SMALL LETTER E WITH ACUTE(U+00E9)LATIN SMALL LETTER E(U+0065)COMBINING ACUTE ACCENT(U+0301)과 합친 것과 표준적으로 갖습니다. 두 확장된 서기소 군집은 é라는 형태의 문자를 출력하므로 표준적으로 동등합니다.

// "Voulez-vous un café"는 LATIN SMALL LETTER E WITH ACUTE를 사용한다.
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"

// "Voulez-vous un café"는 LATINE SMALL LETTER E와 COMBINING ACUTE ACCENT를 사용한다.
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"

if eAcuteQuestion == combinedEAcuteQuestion {
  print("이 두 문자열은 동등하다.")
}
//   "이 두 문자열은 동등하다."가 출력됨

이와 반대로, LATIN CAPITAL LETTER A(U+0041, 또는 "A")는 영어에서 쓰이며 이 문자는 러시아 문자인 `CYRILLIC CAPITAL LETTER A(U+0410, or "A")와 같지 않습니다. 두 문자가 똑같은 값으로 보이나 언어적으로 다른 의미를 지니기 때문입니다.

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

if latinCapitalLetterA != cyrillicCapitalLetterA {
  print("두 문자열은 일치하지 않는다.")
}
// "두 문자열은 일치하지 않는다."가 출력됨

접두사와 접미사 동일성

문자열이 특정한 접두사나 접미사를 가졌는지 확인하려면 StringhasPrefix(_:)hasSuffix(_:) 메서드를 호출하면 됩니다. 두 메서드 모두 하나의 String 인자 값을 사용하고 Boolean 값을 반환합니다.

아래의 예제는 문자열의 배열이 셰익스피어의 _로미오와 줄리엣_의 처음과 두 번째 장의 장소를 나타냅니다.

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

이 연극의 1 장에 해당하는 장면 개수를 세려면 romeoAndJuliet배열을 hasPrefix(_:) 메서드를 사용하세요.

var act1SceneCount = 0
for scene in romeoAndJuliet {
  if scene.hasPrefix("Act 1") {
    act1SceneCount += 1
  }
}
print("연극의 1 장에는 \(act1SceneCount) 씬이 있다. ")
// "연극의 1 장에는 5 씬이 있다."가 출력됨

hasSuffix(_:) 또한 Capulet’s mansionFriar 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 장면; \(cellCount) cell 장면")
// 6 mansion 장면; 2 cell 장면

참고

hasPrefix(_:)hasSuffix(_:) 메서드는 각 문자열내의 확장된 서기소 군집 사이를 비교하는 문자 단위로 동등하는가를 평가합니다.

문자열의 유니코드 표현

유니코드 문자열이 텍스트 파일로 작성되거나 다른 저장소에 저장될 때, 해당 스트링의 유니코드 스칼라는 여러 유니코드 방식의 인코딩 형태 중 하나로 인코딩됩니다. 각각의 형태는 문자열을 코드 유닛(code units)이라고 불리는 작은 청크 단위로 문자열을 인코딩합니다. 이것은 문자열을 8-bit 코드 유닛으로 인코딩하는 UTF-8가 대표적입니다. UTF-16은 16-bit 코드 유닛으로 문자열을 인코딩합니다. UTF-32는 32-bit의 코드 유닛을 사용합니다.

스위프트는 문자열의 유니코드 표현법을 접근할 수 있는 여러 방법을 지원합니다. for-in 문을 이용하여 문자열을 순회하여 각각의 문자 값을 확장된 서기소 군집의 유니코드로 접근할 수 있습니다.

또 다른 방법으로는, 3 가지의 유니코드 법칙을 따르는 형식의 String 값이라면 접근할 수 있습니다.

  • UTF-8 코드 유닛의 모음
  • UTF-16 코드 유닛의 모음
  • 21-bit 유니코드 스칼라 값의 모음, UTF-32의 인코딩 형태와 동일함(문자열의 unicodeScalars 속성으로 접근)

아래의 예제는 다음의 문자들로 이루어진 문자열의 다른 형식들을 보여줍니다. D, o, g, !!(DOUBLE EXCLAMATION MARK, 유니코드 스칼라로는 U+203C) 그리고 또한 🐶(DOG FACE 또는 U+1F436 유니코드 스칼라)

let dogString = "Dog!!🐶"

UTF-8 형식

String의 UTF-8 형식에 접근하려면 문자열의 utf8 속성을 순회하세요. 이 속성은 String.UTF8View의 타입입니다. String.UTF8View 타입은 부호가 없는 8 비트(UInt8) 값의 모음입니다.

../_images/UTF8_2x.png

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

위의 예제는, 처음의 세개의 codeUnit 값(68, 11, 103)은 문자 D,o,g를 나타냅니다. 이것은 ASCII 형식과 같은 UTF-8 형식입니다. 다음의 세개의 코드유닛 값인 (226, 128, 188)은 따옴표 두개 문자를 표현하기 위한 UTF-8형식으로서, 크기는 3 바이트를 차지합니다. 마지막의 코드 유닛 값인 (240, 159, 144, 182)는 4 바이트만큼을 차지합니다.

UTF-16 형식

String을 UTF-16 형식을 접근하려면 Stringutf16 속성을 순회하세요. 이 속성은 String.UTF16View의 타입으로서, 부호 없는 16 비트 값의 모음입니다.

../_images/UTF16_2x.png

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

위 예제에서 처음 세 개의 코드 유닛 값인 (68, 111, 103)은 각각 D, o, g를 표현합니다. 이 문자들의 UTF-16 코드 유닛은 이 문자열의 UTF-8형태와 같은 값을 가집니다.

다음의 4개의 코드 유닛 값인 (8252)는 16진수로 203C와 같습니다. 이것은 따옴표 두개를 표현하는 U+203C 유니코드 스칼라 를 표현합니다. UTF-8과 다르게 UTF-16에서는 하나의 코드 유닛으로 표현되었다는 점을 주목하세요.

다음 5번째와 6번째 코드 유닛인 (55357, 56374)는 한 쌍의 UTF-16 형태로 DOG FACE 문자를 표현합니다. 이 값은 high-surrogate값인 U+D83D(10진수로 55357)과 low-surrogate값인 U+DC36(10진수로 56374)를 가집니다.

유니코드 스칼라 형식

String 값을 유니코드 스칼라 형식으로 접근하려면 StringunicodeScalars 속성을 순회하세요. 이 속성은 UnicodeScalarView의 타입이며 UnicodeScalar 값의 집합입니다.

각각의 UnicodeScalarvalue 속성을 가집니다. value는 스칼라의 UInt32의 21 비트 값을 반환합니다.

../_images/UnicodeScalar_2x.png

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

처음 세개의 UnicodeScalar값인 (68, 111, 103)의 value 속성은 각각 D, o, g를 표현합니다.

네 번째 코드유닛의 값인 (8252)는 십진수로서, 16진수인 203C와 동일합니다. 유니코드 스칼라 U+203C로 표현됩니다.

다섯 번째와 마지막 UnicodeScalar인 128054는 16진수로 1F436과 동일합니다. DOG FACE문자인 유니코드 스칼라 U+1F436을 표현합니다.

각각의 UnicodeScalar 값은 새로운 String 값으로 생성될 수 있습니다. 이를 문자열 삽입에 사용할 수 있습니다.

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