192 lines
8.8 KiB
Markdown
192 lines
8.8 KiB
Markdown
---
|
||
title: Строковые шаблоны и вывод
|
||
description: Интерполяция строк, raw strings, trimIndent, println и форматированный вывод
|
||
tags:
|
||
- kotlin
|
||
- syntax
|
||
---
|
||
|
||
## Шаблоны строк
|
||
Вместо конкатенации - подстановка значений прямо в строку:
|
||
```kotlin
|
||
val name = "Kotlin"
|
||
val version = 2.4
|
||
|
||
println("Привет, $name!") // Привет, Kotlin!
|
||
println("Версия: $version") // Версия: 2.4
|
||
println("Длина имени: ${name.length}") // Длина имени: 6
|
||
println("Сумма: ${2 + 3}") // Сумма: 5
|
||
```
|
||
|
||
Правила:
|
||
- `$имя` - для простой переменной
|
||
- `${выражение}` - для всего остального: вызовов, свойств, арифметики
|
||
- вызывается `toString()` подставляемого значения
|
||
- `null` подставляется как текст `null`
|
||
|
||
```kotlin
|
||
val x: String? = null
|
||
println("значение: $x") // значение: null
|
||
```
|
||
|
||
Фигурные скобки можно ставить всегда - многие так и делают для единообразия.
|
||
|
||
## Экранирование
|
||
```kotlin
|
||
println("цена: \$100") // цена: $100
|
||
println("перенос\nстроки")
|
||
println("табуляция\tтут")
|
||
println("кавычка \" внутри")
|
||
println("слеш \\ внутри")
|
||
println("символ \u00A9") // ©
|
||
```
|
||
Поддерживаются `\t`, `\b`, `\n`, `\r`, `\'`, `\"`, `\\`, `\$`, `\uXXXX`.
|
||
|
||
## Raw strings
|
||
Три кавычки - никакого экранирования, переносы сохраняются как есть:
|
||
```kotlin
|
||
val path = """C:\Users\name\file.txt""" // слеши не экранируются
|
||
val json = """
|
||
{
|
||
"name": "Kotlin",
|
||
"stable": true
|
||
}
|
||
"""
|
||
```
|
||
Удобно для regex, JSON, SQL, HTML.
|
||
|
||
Шаблоны `$` в raw strings **работают**:
|
||
```kotlin
|
||
val user = "admin"
|
||
val query = """SELECT * FROM users WHERE name = '$user'"""
|
||
```
|
||
Чтобы вставить настоящий `$`, используют подстановку символа:
|
||
```kotlin
|
||
val price = """цена: ${'$'}100""" // цена: $100
|
||
```
|
||
|
||
## trimIndent и trimMargin
|
||
Raw string сохраняет отступы исходного кода - их убирают явно:
|
||
```kotlin
|
||
val text = """
|
||
Первая строка
|
||
Вторая строка
|
||
""".trimIndent()
|
||
// "Первая строка\nВторая строка" — общий отступ убран,
|
||
// пустые первая и последняя строки удалены
|
||
|
||
val bar = """
|
||
|Первая
|
||
|Вторая
|
||
""".trimMargin() // по умолчанию маркер |
|
||
```
|
||
`trimIndent()` вычисляет минимальный общий отступ непустых строк и срезает его у всех.
|
||
|
||
## println и print
|
||
```kotlin
|
||
print("без переноса")
|
||
println("с переносом")
|
||
println() // пустая строка
|
||
println(listOf(1, 2, 3)) // [1, 2, 3]
|
||
```
|
||
Это функции стандартной библиотеки, доступны без импорта. Под капотом - `System.out`.
|
||
|
||
## Форматированный вывод
|
||
Kotlin использует механизм форматирования Java:
|
||
```kotlin
|
||
import java.util.Locale
|
||
|
||
fun main() {
|
||
println("%05d".format(42)) // 00042
|
||
println("%-10s|".format("left")) // left |
|
||
println(String.format("%s = %d", "x", 5)) // x = 5
|
||
|
||
// вещественные числа и разряды зависят от локали — её лучше задавать явно
|
||
println("%.2f".format(Locale.US, 3.14159)) // 3.14
|
||
println("%,d".format(Locale.US, 1234567)) // 1,234,567
|
||
}
|
||
```
|
||
|
||
> [!warning] Локаль по умолчанию
|
||
> `format` без явной локали берёт системную. На русской локали получится `3,14` и `1 234 567` - с запятой и неразрывными пробелами. Если строка идёт в JSON, SQL или лог, это ломает парсинг. Для машинных форматов всегда передавайте `Locale.US` (или `Locale.ROOT`).
|
||
|
||
Основные спецификаторы:
|
||
|
||
| Спецификатор | Значение |
|
||
|---|---|
|
||
| `%d` | целое число |
|
||
| `%f` | вещественное |
|
||
| `%.Nf` | вещественное с N знаками |
|
||
| `%s` | строка (через `toString()`) |
|
||
| `%b` | boolean |
|
||
| `%x` | шестнадцатеричное |
|
||
| `%e` | экспоненциальная запись |
|
||
| `%n` | перенос строки платформы |
|
||
| `%%` | символ процента |
|
||
|
||
Флаги ширины: `%10d` - по правому краю, `%-10d` - по левому, `%010d` - с ведущими нулями.
|
||
|
||
## Чтение ввода
|
||
```kotlin
|
||
val line = readLine() // String? — null при конце потока
|
||
val text = readln() // String — бросает исключение при EOF
|
||
val n = readln().toInt() // падает на некорректном вводе
|
||
val safe = readln().toIntOrNull() ?: 0 // безопасный разбор
|
||
```
|
||
`readln()` появился в Kotlin 1.6 и удобнее, когда ввод гарантирован.
|
||
|
||
## Конкатенация
|
||
Работает, но в шаблонах почти не нужна:
|
||
```kotlin
|
||
println("Hello" + " " + "world") // Hello world
|
||
println("Hello" + 5 + 10) // Hello510 — слева направо
|
||
println("Hello" + (5 + 10)) // Hello15
|
||
```
|
||
Важно: складывать можно только когда **слева** строка. У `Int` нет `plus(String)`, поэтому в Kotlin не скомпилируется то, что в Java работает:
|
||
```kotlin
|
||
// println(5 + 10 + "Hello") // ошибка: Int.plus ожидает Int, а не String
|
||
println("${5 + 10}Hello") // 15Hello — через шаблон
|
||
```
|
||
Для сборки строки в цикле - `StringBuilder` или `buildString`:
|
||
```kotlin
|
||
val result = buildString {
|
||
repeat(3) { append("ха") }
|
||
}
|
||
println(result) // хахаха
|
||
```
|
||
Подробнее: [Строки](../Работа%20с%20данными/Строки.md)
|
||
|
||
> [!info] Отличия от Java
|
||
> | Java | Kotlin |
|
||
> |---|---|
|
||
> | `System.out.println(x)` | `println(x)` |
|
||
> | `"a" + x + "b"` | `"a${x}b"` |
|
||
> | `String.format("%d", n)` | `"%d".format(n)` |
|
||
> | text blocks `"""` (Java 15+) | raw strings `"""` + шаблоны |
|
||
> | `.stripIndent()` | `.trimIndent()` |
|
||
> | `new Scanner(System.in)` | `readln()` |
|
||
>
|
||
> Шаблоны - не просто синтаксический сахар: компилятор собирает их через `StringBuilder` или `invokedynamic`, то есть без промежуточных строк. Писать `"a" + b + "c"` ради производительности не нужно.
|
||
>
|
||
> Сравнить: [Форматированный вывод в Java](../../../Программирование%20на%20Java/Синтаксис%20и%20типы/Базовый%20синтаксис/Форматированный%20вывод.md)
|
||
|
||
> [!warning] Подводные камни
|
||
> **`$` перед словом всегда считается шаблоном.** Строка `"цена: $100"` скомпилируется (цифра не может начинать имя), а `"$name_suffix"` - нет: Kotlin ищет переменную `name_suffix`. Нужны скобки: `"${name}_suffix"`.
|
||
>
|
||
> **`trimIndent()` чувствителен к смешению табов и пробелов** - отступ считается по символам, таб и четыре пробела для него разные вещи.
|
||
>
|
||
> **`$` в regex внутри raw string.** `"""^\d+$"""` работает, потому что после `$` идёт конец строки. Но `"""$\d"""` компилятор попытается разобрать как шаблон.
|
||
>
|
||
> **Шаблон вызывает `toString()` у массива, а не его содержимое:**
|
||
> ```kotlin
|
||
> val arr = arrayOf(1, 2, 3)
|
||
> println("$arr") // [Ljava.lang.Object;@1b6d3586
|
||
> println(arr.contentToString()) // [1, 2, 3]
|
||
> println("${arr.joinToString()}") // 1, 2, 3
|
||
> ```
|
||
>
|
||
> **`readLine()` возвращает `String?`.** Вызов `readLine().toInt()` не скомпилируется - нужен `readln()` или обработка null.
|
||
|
||
Дополнительно: [Строки](../Работа%20с%20данными/Строки.md)
|
||
Далее: [Пакеты и импорты](Пакеты%20и%20импорты.md)
|