Swift String 다루기
09 Jul 2017Swift String 형태
이번 포스팅에서는 Swift의 String에 대해 알아보고자 합니다. Swift에서 문자열을 다음과 같은 형태를 지닙니다.
기존 Swift3에서는 한 줄 문자열밖에 없었는데, Swift4에서 여러 줄을 통해 이뤄진 문자열을 """
를 통해 생성할 수 있습니다.
여러 줄을 통해 이뤄진 문자열의 앞에 있는 whiteSpace는 출력할 때 생략됩니다. 다만, 시작하는 문장보다 뒷쪽으로 들여쓰기가 들어가는 경우, 해당 들여쓰기는 적용됩니다.
Extended Grapheme Clusters(확장적 문자소 집합)
Swift에서 하나의 Character는 Single Extended Grapheme Clusters
를 나타냅니다. Extended Grapheme Clusters
라는 것은 사람이 읽을 수 있는 하나의 문자를 지칭합니다. 여기서 “사람이 읽을 수 있다”의 의미는 영어에만 적용되는 것이 아니라, 다른 나라의 언어도 포함합니다. Apple의 공식문서에서 한글을 예로 드는데요. 이를 살펴보겠습니다.
decomposed
는 놀랍게도 Character 타입에 3개의 scala
(이 때 scala
라는 것은 최소 의미를 담은 Character 값(ex: \u{D55C}
)들을 의미합니다.)가 들어갑니다. 즉, Extended Grapheme Clusters에서는 1 scala = 1 Character 가 아니라, 1 Character = 1개 혹은 여러개의 scala 가 되는 것입니다. 인덱스를 통해서 String을 다룰 수 없는 이유가 여기서 나타납니다. 각각의 Character가 여러 개의 scala 값을 지닐 수 있기 때문에 동일한 메모리로 한정된 저장공간을 가지기 어렵습니다. 그렇기 때문에 String을 만들 때 Character별로 동일한 크기의 메모리를 할당할 수 없고, 이는 인덱스를 통한 접근을 불가능하게 만드는 것입니다.
Swift의 String 접근하기
Swift에서는 String[Int]
를 통해 개별 문자에 접근하기 어렵기 때문에, Swift에서 제공하는 메소드들을 사용해야만 합니다. Swift에서는 String.index
메소드를 통해서 개별문자 혹은 범위로의 접근을 할 수 있습니다. 가장 기본이 되는 2가지 메소드는 startIndex
와 endIndex
입니다.
개별 Character 접근하기(startIndex, endIndex)
위와 같은 경우 str.startIndex
는 0
을 반환하고, str.endIndex
는 5
를 반환합니다. 조심해야할 부분은 endIndex
값이 4가 아니라 5라는 점입니다. Hello
는 5글자이고, 0부터 시작하면 4까지인데, endIndex
는 5라는 것이죠. 즉, endIndex
는 전체 String의 길이를 반환한다는 것을 알 수 있습니다. 위의 메소드들을 통해서 String의 Character들을 접근할 수 있습니다.
위의 예시처럼 str[메소드를 통해 접근한 특정 인덱스]
의 표현을 통해 개별 Character에 접근할 수 있습니다.(여기서 숫자로 접근하면 에러입니다.) 그래서, str[str.startIndex]
이 H
를 출력하는 것은 직관적입니다. 하지만, str[str.endIndex]
의 경우 상황이 조금 다릅니다. 왜냐하면 전체 문자는 0부터 4까지인데, str.endIndex
는 5이기 때문이죠. 그래서 str[str.endIndex]
는 에러이고, 마지막 문자에 접근하기 위해서는 endIndex
앞의 인덱스에 대한 접근이 필요합니다.
Swift는 이와 같은 앞쪽 혹은 뒤쪽 문자에 대한 접근을 위해 str.index[before:]
와 str.index[after:]
메소드를 제공합니다. 여기서는 endIndex
앞의 문자를 접근해야 하므로, str.index(before: str.endIndex)
이 마지막 문자 o
를 가리키는 인덱스가 됩니다. 비슷한 원리로 str.index(after: str.startIndex)
를 사용하면, H
다음의 문자 e
에 접근할 수 있습니다.
개별 Character 접근하기(offsetBy)
startIndex
와 endIndex
는 String의 시작과 끝(흑은 그 앞뒤 인덱스)으로의 접근만 가능할 뿐, 중간에 있는 문자에 대한 접근은 어렵습니다. 그래서 Swift는 중간에 있는 문자에 접근하기 위한 index(_:offsetBy:)
메소드를 별도로 제공합니다.
사용방법은 다음과 같습니다.
위의 예시에서 4개의 출력 중 위의 2개는 str.startIndex
를 시작지점으로 정한 것입니다. str.startIndex
에서 0만큼 떨어진 것은 자기 자신이므로 H
가 되고, 6만큼 떨어진 것은 W
입니다.(자기자신의 인덱스에서 6을 더한 것) 다음으로 아래 2개의 예시는 뒷쪽에서 접근하는 방법입니다. str.endIndex
는 전체 String의 가장 마지막 값입니다. 여기서 str.endIndex
은 전체 길이를 반환하므로 -1을 더하면 전체 String의 마지막 문자를 지칭하는 인덱스가 됩니다. 이와 같은 원리로 -3을 더하면 r
이 나오는 것입니다.
루프를 통해 전체 String에 접근하기
다음은 루프를 통한 String 접근입니다. 크게 3가지 방법이 있습니다. 첫 번째는 str
를 사용하여 개별 문자(value)에 접근하는 방식입니다.
다음은 indice
를 활용하여 인덱스에 접근하는 방식입니다.
마지막으로 인덱스와 개별 문자에 동시에 접근하는 방식입니다.
Swift String Insert, Remove
Swift의 String Insert는 어떤 내용을 어떤 곳에 할 것인지를 통해 나타납니다. 이 때 사용하는 메소드는 insert(_:at:)
(insert(contentsOf:at:)
)입니다.
위처럼 개별 Character를 삽입할 때는 contentsOf
를 사용하지 않아도 됩니다.
String Remove의 경우에도 Insert와 유사한데, 문자를 삭제할 위치 및 범위를 지정해주면 됩니다. 이 때는 remove(_:at:)
(removeSubrange(_:)
) 메소드를 사용합니다.
여기서 remove(_:at:)
메소드는 직관적으로 이해할 수 있습니다. 반면 range
의 경우에는 아무 range
나 사용해서는 안됩니다.(하나의 배열도 range
가 될 수 있습니다.) 여기서 말하는 range
는 String.index 를 사용하여 닫힌 range
를 의미합니다. 즉 위의 예시처럼, str.index
혹은 str.endIndex
같은 것들로 닫힌 range
를 지칭하는 것입니다.
Prefix와 Suffix(접두사, 접미사)
hasPrefix(_:)
메소드와 hasSuffix(_:)
메소드는 String의 앞쪽 혹은 뒷쪽에 찾고자 하는 문자가 있는지를 확인할 수 있도록 해주는 메소드입니다. 특정 문자열이 앞에서부터(혹은 뒤에서부터) 포함되어 있는지 확인할 때 좋은 메소드라고 생각됩니다.
Substring
Substring
은 말그대로 String의 일부를 지칭합니다.
다음과 같은 두 개의 string이 있다고 할 경우 str은 str2에 포함된 패턴으로 일종의 substring이라고 생각할 수 있습니다. 하지만 두 string은 서로 다른 메모리를 사용하기 때문에 메모리 측면에서는 다른 서로 포함 관계가 아닙니다. 이번에 추가된 Substring
을 통해서는 하나의 String에서 내용으로 보나, 메모리 측면에서 보나 포함된 Substring
을 만들 수 있게 해줍니다. Substring은 원래 string에서 인덱스의 범위를 지정해주는 형태로 만들 수 있습니다.
위에서 만든 subStr
은 새로운 메모리를 해당 변수에 할당하지 않고, 기존의 str 변수의 메모리를 재사용합니다. 이런 메모리 재사용은 메모리 할당 비용을 줄여줍니다. 하지만, 이와 같은 활용은 장기로 string을 저장하는 경우에 적합하지 않으므로 장기로 string을 사용할 경우 새로운 string을 만드는 것이 좋습니다.
참고자료
- Apple Inc. The Swift Programming Language (Swift 4 Beta)