여덟 번째, 문자열입니다.
Kotlin에서 문자열은 String 타입으로 표현합니다.
JVM에서 UTF-16으로 인코딩된 문자열을 담고 있는 String 객체는 글자당 대략 2 바이트를 사용합니다.
일반적으로 문자열은 큰따옴표로 묶은 (0개 이상의) 문자의 연속체입니다.
val str = "abcd 123"
문자열의 요소들은 인덱스 연산자로 s[i] 식으로 접근할 수 있는 문자들입니다. 이 문자들은 다음과 같이 for 반복문으로 열거할 수 있습니다.
fun main() {
val str = "abcd"
for (c in str) {
println(c)
}
}
/* 결과
a
b
c
d
*/
문자열은 불변입니다. 문자열을 한 번 초기화하면 값을 변경하거나 새로운 값을 할당할 수 없습니다. 문자열을 변경화는 모든 연산은 변환 결과를 반환할 때 기존의 문자열은 그 대로 둔 체 새로운 String 객체를 반환합니다.
fun main() {
val str = "abcd"
// str이 참조하고 있는 "abcd" 대한 대문자 형태를
// 새로운 String 객체를 만들고 출력
println(str.uppercase())
// ABCD
// 원래 문자열은 그대로 유지됨
println(str)
// abcd
}
(당연한 얘기이기는 하지만) 혼동하지 말아야 할 것은 문자열을 참조하는 변수가 불변성이 아니라 문자열 자체가 불변성이라는 것입니다.
문자열을 연결할 때는 + 연산자를 사용합니다. 첫번째 피연산자의 표현식이 문자열이기만 하면, 두 번째 피연산자는 다른 타입이어도 연결됩니다.
fun main() {
val s = "abc" + 1
println(s + "def")
// abc1def
}
대부분의 경우 문자열 연결 보다는 (아래에서 설명하고 있는) 문자열 템플릿이나 여러줄 스트링을 사용하는 것이 좋습니다.
문자열 리터럴
Kotlin에는 두 가지 형태의 문자열 리터럴이 있습니다.
- 이스케이프된 문자열 (Escaped strings) - 일반 문자열
- 여러줄 문자열 (Multiline strings)
이스케이프된 문자열
이스케이프된 문자열은 이스케이프된 문자를 포함할 수 있습니다. 다음은 이스케이프된 문자열 예입니다.
val s = "Hello, world!\n"
이스케이프 시키는 것은 전통적인 방식인 역슬래시(\)를 사용합니다. 지원되는 이스케이프된 문자는 여기에서 확인할 수 있습니다.
이스케이프 문자(Escape charater)
이스케이프 문자는 해당 문자 뒤에 오는 문자를 다른 의미로 해석하게 해 주는 문자입니다. 보통 역슬래시가 많이 사용됩니다. 그리고, 이스케이프 문자가 붙은 문자 형태(예: \n, \uFF00)를 이스케이프 문자가 붙은 연속체라 하여 이스케이프 시퀀스라고 부릅니다.
탈출이라는 이스케이프라는 단어를 사용한 것은 '\''나 "\"" 같이 문자나 문자열을 표시하는 따옴표를 내부에 사용하기 위해 탈출시킨다는 의미에서 비롯된게 아닌가 하는 생각이 듭니다.
탈출 문자라고 번역하는 경우도 있는 거 같습니다만, 본 연재에서는 이스케이프라고 얘기하도록 하겠습니다.
여러줄 문자열
여러줄 문자열은 개행 문자 및 임의의 텍스트를 포함할 수 있습니다. 여러줄 문자열은 세개의 큰따옴표(""")로 구분합니다. 여러줄 문자열 내에서는 이스케이프 시키는 것이 필요 없고, 개행 문자나 기타 다른 문자들을 자유롭게 사용할 수 있습니다.
val text = """
for (c in "foo")
print(c)
"""
여러줄 문자열에서 앞에 붙는 공백을 제거하려면, 다음과 같이 trimMargin() 함수를 사용합니다.
var text = """
|Tell me and I forget.
|Teach me and I remember.
|Involve me and I learn.
|(Benjamin Franklin)
""".trimMargin()
println(text)
text = """
Tell me and I forget.
Teach me and I remember.
Involve me and I learn.
(Benjamin Franklin)
"""
print(text)
/* 결과
Tell me and I forget.
Teach me and I remember.
Involve me and I learn.
(Benjamin Franklin)
Tell me and I forget.
Teach me and I remember.
Involve me and I learn.
(Benjamin Franklin)
*/
기본적으로 | 가 접두어로 사용됩니다. 다른 문자를 사용하고 싶은 경우 trimMargin(">") 처럼 원하는 문자열을 인수로 지정하면 됩니다. trimMargin()외에 trimIndent()라는 함수도 있습니다. 상세 내용은 여기를 참고하세요.
문자열 템플릿
문자열 리터럴은 템플릿 표현식 - 평가되어서(evaluation) 해당 결과가 문자열에 연결되는 코드 조각을 포함할 수 있습니다. 템플릿 표현식은 $ 기호로 시작하고 그 뒤에 사용하려는 이름이 붙습니다.
fun main() {
val i = 10
println("i = $i")
// i = 10
}
또한, $뒤의 내용을 중괄호로 묶을 수도 있습니다. $뒤가 단순한 이름이 아닌 표현식인 경우 중괄호로 묶어줘야 합니다(코딩 규칙에서는 단순한 이름이 아닌 경우에만 중괄호를 사용하라고 권고하고 있습니다).
fun main() {
val s = "abc"
println("$s.length is ${s.length}")
// abc.length is 3
}
템플릿 표현식은 여러줄 문자열과 이스케이프된 문자열 모두에서 사용할 수 있습니다. 여러줄 문자열에서 템플릿 표현식으로의 의미가 아닌 순수한 $로 시작하는 (식별자의 시작 부분으로 허용된) 심볼을 써야 하는 경우에는 다음과 같이 합니다.
// 이 경우는 _9.99의 경우 _라는 문자로 시작하기 때문에 Kotlin의 식별자 규칙에 부합합니다.
// 그래서, 지금 같이 하지 않고 $를 붙이는 경우 문자열 바깥에서 _9.99라는 식별자를 찾는 평가작업을 하게됩니다.
var price = """
${'$'}_9.99
"""
println(price)
// Kotlin은 숫자로 시작하는 식별자를 허용하지 않기 때문에, 9.99는 식별자로 사용될 수 없습니다.
// 그래서, 그냥 $를 붙여도 평가되지 않고 그대로 달러 문자로 인식됩니다.
print = """
$9.99
"""
println(price)
/* 결과
$_9.99
$9.99
*/
프로그래밍 언어에서 식별자(identifier)는 (간단히 얘기해서) 언어의 엔티티들(변수, 데이터 타입, 라벨, 함수 등등)을 명명하는 어휘적 토큰(또는 심볼)입니다. 말이 좀 어려울 수 있는데, var price = ".." 에서 price는 변수명을 나타내는 식별자입니다.
좀 더 자세히 알고 싶은 경우 https://en.wikipedia.org/wiki/Identifier_(computer_languages)를 참고하시기 바랍니다.
평가(evaluation)
프로그래밍 언어쪽에 보면 평가(evaluation)하다라는 말을 자주 볼 수 있습니다. 본 연재에도 여러번 사용됩니다. 단어대 단어로 번역하기 때문에 evaluation에 대해 '평가'라는 단어를 사용하는데요, 의미는 이렇게 생각하면 됩니다. 해당 내용을 코드로서 평가(이해)하여 컴파일 과정을 거쳐 실행 후 실제 결과를 적용한다. 보통 실행시간에 이루어집니다.
"$a 는 b와 다르다"라는 문자열 템플릿이 평가된다고 하면 실행 시간에 해당 부분이 코드로서 평가되어 컴파일 과정을 거쳐 실행된 후 결과 문자열이 적용된다고 이해하시면 됩니다.
문자열 서식화 (String formatting)
String.format()을 사용하여 문자열을 서식화하는 것은 단지 Kotlin/JVM에서만 가능합니다. 즉, 멀티플랫폼 지원 언어인 Kotlin에서 다른 플랫폼 환경에서는 String.format()을 사용할 수 없습니다.
특정한 요구사항에 맞게 문자열을 서식화 할 때는 String.format() 함수를 사용합니다.
String.format() 함수는 서식 문자열과 하나 이상의 인수를 받습니다. 서식 문자열은 인수에 대응하는 플레이스 홀더(placeholder)를 포함합니다. 플레이스 홀더는 %로 시작하고 그 뒤로 서식 지정자(format specifier)가 붙습니다. 서식 지정자는 해당하는 인수에 대한 서식을 지정하는 명령어로서 플래그, 너비, 정밀도, 변환 타입 등으로 구성됩니다. 서식 지정자의 구성 요소 전체가 출력되는 모양을 정하게 됩니다. 공통 서식 지정자에는 정수를 위한 %d, 부동 소수점 숫자를 위한 %d, 문자열을 위한 %s 등이 있습니다. 또한, 서식 문자열 안에서 같은 인수를 (다른 형태들로 나타내기 위해) 여러 번 참조하기 위한 인수_색인번호$ 구문도 있습니다. 인수의 색인 번호이기 때문에, 첫번째 인덱스 즉, 0번 인덱스는 문자열 서식이므로, 실제로 서식에 사용되는 인수 색인 번호는 1번부터 시작합니다. 예) 1$, 2$
상세한 이해와 다양한 형식 지정자들을 확인하려면, Java의 관련 문서를 참고하시기 바랍니다.
사용 예를 살펴보겠습니다.
fun main() {
// 정수. 너비를 일곱자로 하고 인수가 해당 너비에 만족하지 못하는 경우 0으로 앞을 채웁니다.
val integerNumber = String.format("%07d", 31416)
println(integerNumber)
// 0031416
// 부동 수숫점
// + 기호를 붙이고, 네자리의 소수 부분을 지정합니다.
val floatNumber = String.format("%+.4f", 3.141592)
println(floatNumber)
// +3.1416
// 두 개의 문자열을 대문자화합니다.
val helloString = String.format("%S %S", "hello", "world")
println(helloString)
// HELLO WORLD
// 음수를 괄호로 묶어서 표시하고, 같은 수를 괄호 없는 다른 형식으로
// `argument_index$`를 사용(1$ 이스케이프된 문자열 리터럴이기 때문에 \$로 이스케이프)하여 표시합니다.
// %(d 로 인해 마이너스(-)는 표시하지 않고 괄호로 묶습니다.
val negativeNumberInParentheses = String.format("%(d means %1\$d", -31416)
println(negativeNumberInParentheses)
//(31416) means -31416
}
String.format() 함수는 스트링 템플릿과 비슷한 기능을 제공합니다. 하지만, String.format() 함수가 더 다양한 서식 옵션을 사용할 수 있기 때문에 좀 더 다재다능합니다.
부가적으로 서식 문자열을 변수에 할당할 수 있습니다. 이 방법은 - 지역에 맞게 서식을 현지화(localization) 해야하는 경우 같은 - 서식이 변경될 때 유용합니다.
String.format() 함수를 사용할 때는 서식의 플레이스 홀더와 인수들의 수가 일치하는지 주의해야 합니다.
'Kotlin' 카테고리의 다른 글
공식 문서로 배우는 코틀린 - 10. Types - Type checks and casts (0) | 2024.03.05 |
---|---|
공식 문서로 배우는 코틀린 - 9. Basic Types - Arrays (1) | 2024.03.05 |
공식 문서로 배우는 Kotlin - 7. Basic Types - Characters (0) | 2024.03.02 |
공식 문서로 배우는 Kotlin - 6. Basic Types - Booleans (0) | 2024.03.02 |
공식 문서로 배우는 Kotlin - 5. Basic Types - Unsigned integer types (0) | 2024.03.01 |