Files
obsidian/Kotlin/Синтаксис и типы/Базовый синтаксис/Строковые шаблоны и вывод.md
T

192 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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)