본문으로 바로가기
반응형

이 글은 Kotlin In Action을 참고 하였습니다.

더욱 자세한 설명이나 예제는 직접 책을 구매하여 확인 하시기 바랍니다

3.5 문자열과 정규식

코틀린에서 기본 라이브러리에서 자바에서 좀더 확장된 함수들(API)를 제공합니다.

3.5.1 split의 사용

문자열을 분할하기 위해서는 split을 사용합니다.

split 함수는 인자로 정규 표현식을 받습니다.

따라서 "123.456-7.AB".split(""."")를 수행하면 반환값이 없습니다.

정규 표현식에서 "."은 문자열 전체를 나타내기 때문입니다.


이런 잘못된 사용을 막기위해 코틀린에서는 일반문자열을 받는건지, 정규표현식이 들어오는건지를 구분합니다.

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

두 함수의 결과는 "[12, 345, 6, A]" 로 동일 합니다.

toRegex()를 통해 정규표현식인지를 명시적으로 표현하여 인자로 넣어줄 수 있습니다.


3.5.2 3중 따옴표

Java에는 없는 삼중으로 따옴표를 코틀린에서는 제공합니다.
먼저 파일 경로에서 파일 이름, 경로, 확장자를 구분하는 코드를 아래와 같이 두가지로 확인해 보겠습니다.
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) {
    parsePath("/Users/yole/kotlin-book/chapter.adoc")
}

코틀린에서 추가적으로 제공하는 String 확장함수를 써서 파일경로를 분리해 봤습니다.

사실 정규 표현식을 쓸 필요도 없이 API가 잘 만들어져 있네요~

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) {
    parsePath("/Users/yole/kotlin-book/chapter.adoc")
}
같은 예제를 정규표현식으로 표현한 코드 입니다.

"""으로 삼중 따옴표를 사용하면, 역슬래시 / 포함하여 어떤 문자도 따로 escape 할 필요가 없습니다.


3.5.3 여러줄을 삼중 따옴표로 표기

또한 삼중 따옴표를 사용하면 줄바꿈이 있는 텍스트도 쉽게 만들 수 있습니다.

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

fun main(args: Array) {
    println(kotlinLogo.trimMargin("."))
}

위 코드의 결과는

|  //

|//

|/ \

로 출력됩니다.

삼중 따옴표는 내부에 있는 들여쓰기나 줄바꿈등의 모든 문자를 포함합니다.

문자열 앞에 .을 붙이고 trimMargin()을 이용하여 앞에 공백을 없애주는 형태로 사용한다면, 코드상에 출력될 형태를 미리 예상하여 작업할 수 있습니다.

편리하죵~


단! 아래 두가지는 주의해야 합니다.

  • 줄바꿈을 하는 /n 같은 특수문자는 넣을수 없다. (넣더라도 줄바꿈이 되지 않습니다.)
  • escape 문자인 \를 넣고 싶으면 그대로 넣으면 된다. (escape가 아닌 문자로 인식된다

3.6 중첩함수 (local method)

중첩함수란 함수안에 함수를 넣는것을 말합니다.
자바에서는 클래스안에 함수가 존재하고, 함수안에 중첩해서 함수를 넣을수 없지만 코틀린은 가능합니다.

중첩함수는 언제 사용해야 할까요?
가장 좋은 예는 중복을 없애거나, 긴 함수를 분리해 (extract method) 사용하면 좋습니다.

Java에서 긴 함수가 있다면 method를 extract 해서 분리 시킵니다.
1. 긴함수 존재 -> 가독성 문제나 코드 중복 발생여지가 있음.
2. 함수를 extract
3. 작은 method가 증가
4. 작은 method로 인해 코드의 연결관계를 파악하기가 더 어려워짐.

위와 같은 경우 중첩함수를 사용하여 함수안에 함수를 만들면 좋습니다.

class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) {
    if (user.name.isEmpty()) {
        throw IllegalArgumentException(
            "Can't save user ${user.id}: empty Name")
    }

    if (user.address.isEmpty()) {
        throw IllegalArgumentException(
            "Can't save user ${user.id}: empty Address")
    }

    // Save user to the database
}

fun main(args: Array) {
    saveUser(User(1, "", ""))
}
위 예제에서는 User 클래스를 만들어 정보를 담습니다.
saveUser에서는 이름과, 주소가 제대로 들어 있는지를 check 합니다.

체크하는 함수가 늘어날수록 함수내 동일코드의 반복이일어 납니다.
이걸 좀더 예쁘게 수정해 보지요~

1. check부분을 함수내 내부 함수로 만든다
2. saveUser 함수 자체를 User 클래스의 확장함수로 만든다.

class User(val id: Int, val name: String, val address: String)

fun User.validateBeforeSave() { //User class의 확장 함수로 정의
    fun validate(value: String, fieldName: String) {
        if (value.isEmpty()) {
           // Extension function이므로 id값에 바로 접근가능
            throw IllegalArgumentException("Can't save user $id: empty $fieldName")
        }
    }

    validate(name, "Name")
    validate(address, "Address")
}

fun saveUser(user: User) {
    user.validateBeforeSave()

    // Save user to the database
}

fun main(args: Array) {
    saveUser(User(1, "", ""))
}

중첩(로컬)함수는 자신을 감싼 외부 함수의 리소스에 바로 접근할수 있습니다.

  • 로컬 함수는 외부 함수의 모든 파라미터, 내부정의 변수에 자유롭게 접근이 가능하다.

단 중첩의 깊이가 늘어난다면, 코드의 가독성이 더 떨어지므로 한단계의 함수만 중첩시키는걸 권장합니다.


반응형