[Swift] String.Index에 관한 고찰

Martin Kim·2022년 2월 5일
0

스위프트에서 문자열을 다루는 방법은 상당히 까다롭다.
예를 들어 다음과 같은 방법은 스위프트에서 불가능하다.

let string = "Hello"
let first = string[0] //에러 발생

이는 다른 언어에서는 가능하지만 스위프트에서는 불가능하다.
이는 왜냐하면 스위프트에서 어떤 타입의 [] 연산을 하기 위해서는 서브스크립트 메소드가 정의되어 있어야 하는데 문자열의 경우 서브스크립트 메소드가 String.Index 라는 특이한 녀석이 입력으로 들어와야 하기 때문이다.

  • 즉 다시 말하자면 문자열에서는 Int 자료형을 입력으로 받는 서브스크립트가 정의되어있지 않다.

솔직히 짜증난다. 이럴 땐 다른언어 쓰고 싶다.

String.Index

이 타입은 String에서 인덱스를 표현하기 위한 타입이다. 즉 어떤 해당 문자의 위치를 표시하기 위한 타입이다.

위 예제에서의 올바른 해답은 다음처럼 나타낼 수 있다.

let first = string[string.startIndex]  // "H"

스위프트 문자열 타입에서는 본인의 맨 처음 String.Index값를 반환하는 String.startIndex 프로퍼티를 갖고 있다.

String.Index 직접 생성하기

직접 생성하려면 다음과 같이 작성한다.

String.Index(encodedOffset: 0)
  • 이건 UTF-8 encoded Offset으로 인덱스를 생성하는 예제이다. 의미는 0번째 인덱스, 즉 1번째 문자를 가리키는 인덱스이다.

위와 같이 직접 생성해도 되겠지만 가급적 String자체에서 제공되는 프로퍼티나 메소드를 사용하는 것이 좋다고 한다. 왜냐하면 다음과 같은 예제,

string.startIndex
string.endIndex

위 두 인덱스는 제일 처음과 끝을 가리키는 인덱스를 뜻하는데 endIndex의 경우 마지막 문자가 아니라 진짜 아무것도 없는 맨 끝, 마치 C언어에서의 문자열 마지막인 \0의 위치를 반환한다고 한다. 그래서 만약 아래와 같이 쓸 경우 프로그램을 죽여버린다고 한다.

string[string.endIndex]  // SIGABRT

진짜 짜증난다. 왜 이걸 이렇게 설정했는지 궁금하다. 스위프트 설계한 사람은 코테보는 사람 함정 빠뜨릴라고 작정한게 분명하다 ㅡㅡ

위 두 프로퍼티를 이용해 유동적인 인덱스를 만들 수 있다. 다음과 같이 index(after:)index(before:) 를 사용하는게 그것이다.

let secondIndex = string.index(after: string.startIndex)
let second = string[secondIndex] // "e"

let endIndex = string.index(before: string.endIndex)
let last = string[endIndex] // "l"

이런 방법 말고 내가 원하는 건 다른언어들 처럼 특정 위치를 바로 가리키는 것이다. 다음과 같은 방법을 사용하면 된다. 시작 인덱스로부터 2칸 뒤의 위치, 즉 3번째 인덱스를 반환하는 예제이다.

let thirdIndex = string.index(string.startIndex, offsetBy: 2)

인덱스의 시작은 1이 아니라 0인 것을 잊지 말것. 또 offsetBy는 음수가 될 수 있다.

또 매력적인 방법은 아예 문자를 검색해서 해당 인덱스를 찾는 방법이 있다.

"abc123".index(of: "c")

이 메서드는 가장 최초로 발견되는 of 인자의 String.Index 값을 반환한다.

Substring

문자열의 일부분을 잘라낸, 부분문자열을 구하려면 어떻게 할까?

진짜 천만 다행이게도 String에는 String.Index 타입의 범위(Range객체)를 입력으로 받는 서브스크립트 메서드가 구현되어 있다고 한다.

진짜 다행이라고 생각

예를 들어, 첫 글자와 마지막 글자를 제외한 문자열을 구하기 위해선?

let start = string.index(after: string.startIndex)
let end = string.index(string.endIndex, offsetBy: -2)
let substring = string[start...end] // "ell"

String.Index를 이용해 Range를 만들고 이것을 문자열의 서브스크립트를 이용해 부분문자열을 가리키게 했다.

여기서 중요한 점은 "가리킨다"는 점이다. 왜냐하면 이 부분문자열은 Substring이라는 또 하나의 '타입'이기 때문이다. ㅋㅋ

이럴 땐 자바보다 더 객체지향스러운 것 같다. 물논 자바를 깊게 공부해본건 아니지만

Substring 타입은 특정 문자열의 부분 문자열을 담기 위한 특수한 타입이다. 하지만 String 타입이 아니므로
문자열이 필요한 곳에 사용하려면?

let realSubstring = String(substring)

위와 같이 문자열로 쉽게 바꿀 수 있다. 타입의 용도가 전혀 다르기 때문에 타입캐스팅이 안된다.

이외에도 어떤 사람들은 extension을 사용해서 String의 서브스크립트를 Int값을 받도록 개조하기도 하는데 코테에서 그럴 시간이 있을지는 모르겠다. 참고~

참고 http://seorenn.blogspot.com/2018/05/swift-string-index.html

profile
학생입니다

0개의 댓글