본문 바로가기
2023년 이전/kotlin

kotlin - 문자열과 정규식 다루기

by JeongUPark 2020. 2. 20.
반응형

[출처 -  Kotlin In Action] [아래 내용들은 Kotlin In Action을 공부하면서 스스로 정리한 내용입니다] 

코틀린의 문자열과 자바의 문자열은 같기 때문에 서로 넘기고 받아도 별다른 문제가 없습니다. 그리고 코틀린에서는 다양한 확장함수를 제공함으로서 문자열을 더 다양하게 다룰수 있습니다.

 

문자열 나누기

자바에서는 String에 split를 사용하여 문자열을 나눌 수 있었습니다. 근데 자바에서 split를 사용할 때 점(.)을 사용하면 문자열을 분리 할 수 없습니다. "12.345-6.A".split(".") 하면 결과가 빈 배열을 반환합니다. 그 이유는 자바의 split의 구분 문자열은 정규식이기 때문에 점(.)은 모든 문자를 나타내는 정규식으로 해석되기 때문입니다.

그래서 점(.)으로 split를 하려면 "12.345-6.A".split("\\.")으로 합니다.

하지만 코틀린에서는 일반문자열을 받는건지, 정규표현식이 들어오는건지를 구분합니다.

fun main(args: Array<String>) {
    println("12.345-6.A".split("\\.|-".toRegex()))
    println("12.345-6.A".split(".", "-"))
}

이렇게 작성하면 둘다 [12, 345, 6, A] 동일한 결과를 내놓습니다.

toRegex()로 정규식인지를 명시적으로 표현해줍니다. 그리고 두번째 split 처럼 여러 구분 문자열을 지정할 수도 있습니다.

(정규표현식이란 ? 줄여서 정규식이라고 하는데 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어입니다. 더 자세한 설명은 역시 구글링!!)

정규식과 3중 따옴표로 묶은 문자열

다음은 확장함수를 활용하여 파일결로, 파일이름, 파일확장자를 구분하는 코드입니다.

fun parsePath(path: String) {
    val directory = path.substringBeforeLast("/")
    val fullName = path.substringAfterLast("/")

    val fileName = fullName.substringBeforeLast(".")
    val extension = fullName.substringAfterLast(".")

    println("Dir: $directory, name: $fileName, ext: $extension")
}

fun main(args: Array<String>) {
    parsePath("/Users/yole/kotlin-book/chapter.adoc")
}

결과는

Dir: /Users/yole/kotlin-book, name: chapter, ext: adoc

이렇게 나타납니다. .substringBeforeLast은 마지막 구분자 이전까지를 파싱하고 substringAfterLast은 마지막구분 이후를 파싱합니다.

그래서 "/"의 마지막 이전이 Dir가 되고 이후가 파일의 fullname이 됩니다. 그리고 그 fullname에서 "." 이전이 파일 이름이 "." 이후가 파일의 확장자 명이 됩니다.

이렇게 코틀린에서는 정규식을 사용하지 않고도 문자열을 쉽게 파싱할 수 있습니다. 그럼 정규식을 사용한 파싱은 어떻게 되는지 알아 보겠습니다.

 

fun parsePath(path: String) {
    val regex = """(.+)/(.+)\.(.+)""".toRegex()
    val matchResult = regex.matchEntire(path)
    if (matchResult != null) {
        val (directory, filename, extension) = matchResult.destructured
        println("Dir: $directory, name: $filename, ext: $extension")
    }
}

fun main(args: Array<String>) {
    parsePath("/Users/yole/kotlin-book/chapter.adoc")
}

 이 코드는 3중 따옴표로 묶여 있는데 이로서 역슬래시를 포함한 어떤 문자도 이스케이프 할 필요가 없어졌습니다. 즉, \.을 쓰기위해 \\.이 아닌 \.으로 표현 할 수 있습니다.

 

(여기서 이스케이프는 이스케이프 문자는 이스케이프 시퀀스를 따르는 문자들로서 다음 문자가 특수 문자임을 알리는 백슬래시(\)를 사용합니다. 그럼 또 이스케이프 시퀀스는 먼까? 이스케이프 시퀀스(escape sequence)는 컴퓨터와 주변 기기의 상태를 바꾸는 데에 쓰이는 일련의 문자열입니다. 제어 시퀀스(control sequence)라고도 합니다. 예를 들어 \n은 줄 바꿈을 의미 합니다. 더 자세한 설명은 구글링!!)

 

그리고 위의 정규식 """(.+)/(.+)\.(.+)"""은 (.+)은 마지막 슬러시까지 모든 문자와 매치되며, 두번째 (.+)는 마지막 점(.)까지 모든 문자와 매치 됩니다. 그리고 마지막(.+)는 나머지 문자와 매치됩니다.

그렇게 정규식에 의해 path를 매칭 시킨후 .destructured를 통하여 각 프로퍼티에 변수를 대입합니다. 그리고 이 부분을 구조 분해라 합니다. (.destructured에는 component1()~component10() 까지 있는데, 위의 정규식에 의해 component1~component3까지 까지 값이 들어가고, 그 값을 각 프로퍼티 변수에 대입합니다.

println("matchResult.destructured: ${matchResult.destructured.component1()} | ${matchResult.destructured.component2()} | ${matchResult.destructured.component3()}")

추가하시면 확인 하실 수 있으실 것입니다.)

 

 

여러줄 3중 따옴표 문자열

3중 따옴표는 문자열 이스케이프를 피하기 위해서만 사용하지는 않습니다. 3중 따옴표를 이용하여 줄바꿈이 있는 텍스트를 쉽게 문자열로 만들 수 있습니다.

 

val kotlinLogo = """| //
                   .|//
                   .|/ \"""

fun main(args: Array<String>) {
    println(kotlinLogo.trimMargin("."))
}
| //
|//
|/ \

이렇게 할 경우 코드에서 더 직관적으로 표현되어는 것을 볼 수 있습니다. 그리고 만약 저 위에서 점(.)을 뺀다면

| //
                   |//
                   |/ \

이런 결과가 나옵니다. 그 이유는 trimMargin에서 "."를 너었는데 이는 "."이 들여쓰기 구분 문자라는 것을 말합니다.

또한 3중 따옴표를 쓰면 "C:\\Users\\yole\\kotlin-book"을 """C:\Users\yole\kotlin-book""" 처럼 쓸 수 있습니다.

 

반응형