--- 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)