티스토리 뷰

Swift 3.0.1 가이드에 대응하는 정리글을 작성하였습니다!!!

Strings and Characters 정리 최신버전 > http://wlaxhrl.tistory.com/34




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


들어가며

String은 문자열을, Character는 문자를 저장할 수 있는 타입이다. 유니코드 호환이 잘 되고 단순한 문법으로 다룰 수 있다. 특히 문자열 두 개를 연결할 때 + 연산자만 사용하면 되는데, 이 부분이 Objective-C를 생각해보면 참 편해졌다는 생각이 든다. 또한 문자열 출력 시 특정 변수를 포함해야 할 때는 Objective-C와 다르게 문자열 안에 \(변수이름)을 바로 넣어 출력한다. 개인적으로 아직 익숙하지 않아서 그런지 이 부분은 불편하다.

참고로 Swift의 String 타입은 Foundation의 NSString과 연결이 되는데, 각각 서로에게 필요한 API가 있는 경우 사용할 수 있다. (나중에 다룹니다)


String Literals

문자열 리터럴이란 double quote(")로 둘러싸인 문자열을 말한다. String 상수/변수에 이 값을 할당할 수 있다.

let someString = "Some string literal value" // String Literal로 초기화된 변수는 String 타입이 된다.

let lenghtOneString = "a" // 이 경우도 변수는 String 타입. Character 타입이라고 명시하면 Character 타입이 된다.


Initializing an Empty String

빈 문자열이 들어있는 String 타입 변수를 만드는 방법과 빈 문자열 체크방법

var emptyString = "" // 빈 문자열 리터럴을 할당하거나
var anotherEmptyString = String()  // 초기화 문법을 사용하거나
// 두 문자열 모두 똑같이 비어있다.

if emptyString.isEmpty
{
    // anotherEmptyString.isEmpty 도 여기에 들어온다.
    print("Nothing to see here")
}


String Mutability

let으로 만들면 변경 불가, var로 만들면 변경 가능.

var variableString = "Horse"
variableString += " and carriage"
// variableString는 "Horse and carriage"
 
let constantString = "Highlander"
constantString += " and another Highlander"
// compile error. constant string cannot be modified

Objective-C 에서는 NSString, NSMutableString으로 이 여부를 결정했었다. Swift에서는 딱히 String 타입에 국한되지 않고 모든 타입의 상수/변수가 let/var를 통해 처음 만들어지는 순간부터 변경 가능 여부가 결정되어야 한다. 생각해보면 optional 타입도, 처음 만들어지는 순간부터 이 변수에 nil이 들어갈 수 있는지 없는지가 결정되어야 한다. 처음 변수를 만들 때부터 미리 앞을 내다보아야 한다. 편할지 불편할지는 아직 많이 안 써봐서 모르겠다.


Strings Are Value Types

String 은 함수에 전달되거나 새로운 String 변수에 할당되거나 할 때 값이 복사된다. String 의 참조가 아닌 그 안에 들어있는 값만 새로 복사되어 전달된다. 따라서 복사된 값을 변경해도 원본은 영향받지 않게 된다. 이 Value Type이라는 건 뒤에서 또 자세히 다루게 될 텐데, 값 복사본이 전달된다는 특성을 잘 알아야 오류없는 코드를 작성할 수 있을 것 같다.

참고로 Cocoa의 NSString은 이런 경우에 참조가 전달된다.


<참고1 - Value Type의 복사에 대해> Value Type의 할당을 할때는 값 복사가 일어난다고 했다. 그러나 엄밀히 말하면 할당을 하는 시점마다 항상 복사가 일어나는 것은 아니다. 일단 복사는 하지않고 같은 곳만 바라보고 있다가, 값이 변경될 때에 복사를 하게 되는 것이다. 예를들어 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가 var로 선언이 되었다면 Name 프로퍼티는 변경 가능하지만, let으로 선언이 되었다면 Name 프로퍼티는 변경 불가능하게 된다. (Reference Type의 경우에는 var/let 어느 것으로 선언되더라도 내부값 변경 가능하다. 참고로 NSObject를 상속받은 객체, 기타 Class로 선언된 객체들이 Reference Type에 속한다.)


Working with Characters

String의 characters 프로퍼티를 사용하면 String 문자열 값의 각각의 문자에 접근할 수 있다.

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


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

let exclamationMark: Character = "!"


Character 값으로 구성된 배열을 통해 String 타입 변수를 만들 수 있다.

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

String(catCharacters) -> Cat!🐱
String(catCharacters,"Hi?") -> (["C", "a", "t", "!", "🐱"], "Hi?")


Concatenating Strings and Characters

  • String = String + String --> OK
  • String = Character + Character --> Compile error
  • String = String + Character --> Compile error
  • String = String.append(Character) --> OK
  • Character = Character + Character --> Compile error
  • Character = String + String --> Compile error
  • Character = String + Character --> Compile error
정리해보면, String 끼리는 + 연산자를 사용해서 합한다. String에 Character를 더할 때는 append() 를 사용한다. Character 타입에는 + 연산자를 직접적으로 사용할 수 없다.
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

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"


Unicode

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


문자열 리터럴은 다음과 같은 Special Character를 가진다.

  • \0 (null character), \\ (backslash), \t (horizontal tab), \n (line feed), \r (carriage return), \" (double quote) and \' (single quote)
  • \u{n}, n is a 1–8 digit hexadecimal number -> 해당하는 유니코드 스칼라를 출력
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


Swift에서 모든 Character 인스턴스는 하나의 extended grapheme cluster를 가진다. extended grapheme cluster 란, 사람이 읽을 수 있는 하나의 Character가 될 수 있도록 하나 또는 그 이상의 유니코드 스칼라 클러스터이다.

let eAcute: Character = "\u{E9}" // é (유니코드 스칼라 1개)
let combinedEAcute: Character = "\u{65}\u{301}" // e followed by ́
// eAcute is é, combinedEAcute is é (유니코드 스칼라 2개)

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


Counting Characters

String 을 구성하는 Character 개수를 세기 위해 String의 characters 프로퍼티의 count 프로퍼티를 사용한다. 참고로 Character 하나에 포함된 유니코드 스칼라가 2개 이상이라도 그와 관계없이 Character 하나 당 1로 계산된다.

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

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

<note> 같은 Character 라도 포함된 유니코드 스칼라의 수가 다를 수 있다(위에서 다룬 extended grapheme cluster 때문에). 따라서 Swift는 Character마다 차지하는 메모리가 다를 수 있다.


Accessing and Modifying a String

<String Indices>

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

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

  • startIndex : 첫 번째 Character 를 가리키는 index
  • endIndex : 마지막 Character의 한 칸 뒤 index. 따라서 endIndex 에서 한 칸 앞으로 와야 마지막 Character를 가리키게 된다.

Index type에는 다음과 같은 메서드가 있다.

  • predecessor() : 내 바로 전
  • successor() : 내 바로 뒤
  • advancedBy(_:) : 나로부터 N만큼 떨어져있는 곳
let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.startIndex.successor()]
// u
greeting[greeting.endIndex] // error
greeting.endIndex.successor() // error
greeting[greeting.endIndex.predecessor()]
// !
let index = greeting.startIndex.advancedBy(7)
greeting[index]
// a

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

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


<Inserting and Removing>

// String 특정 위치에 Character 삽입하기
var welcome = "hello"
welcome.insert("!", atIndex: welcome.endIndex)
// welcome now equals "hello!”

// String 특정 위치에 다른 String 삽입하기
welcome.insertContentsOf(" there".characters, at: welcome.endIndex.predecessor())
// welcome now equals "hello there!”

// String에서 특정 위치의 Character 지우기
welcome.removeAtIndex(welcome.endIndex.predecessor())
// welcome now equals "hello there"

// String에서 sub string 지우기
let range = welcome.endIndex.advancedBy(-6)..<welcome.endIndex
welcome.removeRange(range)
// welcome now equals "hello”


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



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

Control Flow  (0) 2016.03.03
Collection Types - Dictionary  (0) 2016.03.01
Collection Types - Set  (0) 2016.03.01
Collection Types - Array  (0) 2016.02.28
Basic Operators  (1) 2016.02.28
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함