1. Чтение с консоли, стандартный ввод
В предыдущих лекциях мы познакомились с командами вывода на экран. Для этого мы использовали функции print() и println(). Просто и удобно.
Но, как вы уже, наверное, догадываетесь, одного вывода на экран недостаточно. Цель большинства программ — сделать что-то полезное для пользователя. Поэтому очень часто нужно, чтобы пользователь мог вводить данные с клавиатуры.
В Kotlin есть 2 функции для чтения данных с клавиатуры:
- readln() — возвращает String (или ошибку)
- readLine() — возвращает String или null
Мы начнем с варианта попроще — readln(), а когда разберем работу с null в Kotlin, можно будет попробовать и другие варианты.
2. Функция readln()
Функция readln() умеет считать одну строку из стандартного ввода (то есть с клавиатуры). Обычно это выглядит так:
| Код | Пояснение |
|---|---|
|
Читаем строку текста. |
|
Затем читаем ещё одну строку и преобразуем её в число (Int). |
Выглядит вроде несложно, но так ли все просто на самом деле? Думаю, у вас появилась куча вопросов, и сейчас мы на них ответим. Но для начала продемонстрируем пример полной программы, где используется readln():
fun main() {
val name = readln()
val age = readln().toInt()
println("Name: " + name)
println("Age: " + age)
}
3. Объявление переменных и чтение данных
Первый вопрос — что это за строки val name = readln() и val age = readln().toInt()?
Как вы, надеюсь, уже запомнили, в Kotlin переменные обычно объявляются с помощью val (неизменяемая) или var (изменяемая). Точно так же работает и чтение строки:
val name = readln()
Все, что находится слева от знака равенства — это объявление переменной по имени name. Можно было назвать ее, например, s или userName, или даже keyboardInput. Тогда код выглядел бы так:
val s = readln()
val userName = readln()
val keyboardInput = readln()
Думаю, теперь все стало гораздо понятнее. А код, что находится справа от знака равенства, тоже не содержит ничего сложного:
- readln() — читает строку из консоли;
- .toInt() — пытается преобразовать строку в целое число.
После выполнения этих строк у нас появятся переменные, с помощью которых наша программа сможет использовать введённые пользователем данные.
4. Ввод данных с консоли
Вводить данные с клавиатуры с помощью readln() очень легко.
Чтобы считать с клавиатуры строку, нужна команда:
val str = readln()
Когда программа дойдет до выполнения этой строки, она приостановится и будет ждать, пока пользователь введет данные и нажмет клавишу Enter. После этого все, что ввел пользователь, будет сохранено в переменную str.
Чтобы считать с клавиатуры число, обычно делают так:
val number = readln().toInt()
Тут все аналогично предыдущей команде. Когда программа дойдет до выполнения этой строки, она приостановится и будет ждать, пока пользователь введет данные и нажмет клавишу Enter. После этого все, что ввел пользователь, будет преобразовано в число и сохранено в переменную number.
Если пользователь ввел данные, которые невозможно преобразовать в целое число, в программе возникнет ошибка (обычно это NumberFormatException), и она завершится.
Чтобы считать с клавиатуры дробное число, нужна команда:
val number = readln().toDouble()
Эта команда полностью аналогична преобразованию toInt(), только она преобразует строку в число Double.
Пример программы, которая считывает с клавиатуры два числа и выводит их сумму:
fun main() {
val a = readln().toInt()
val b = readln().toInt()
println(a + b)
}
5. trim(): что делает и зачем нужен
trim() — это функция у String, которая убирает пробельные символы по краям строки: слева и справа. Представьте, что строка — это коврик у входа, а пробелы — грязь на обуви. trim() — это быстро «обстучали ботинки», чтобы дальше по дому (то есть по коду) не разносить мусор.
Главное достоинство trim() в том, что это очень дешёвое действие для мозга программиста: одна функция — и вы уже защитились от половины «почему не парсится?!». Особенно полезно это перед toInt()/toIntOrNull(), потому что строка " 42 " выглядит как число, но для парсера это уже не «чистый» формат.
Сейчас мы ещё не читаем ввод из консоли (это будет следующая лекция), поэтому будем имитировать «сырой ввод» обычными строковыми литералами.
fun main() {
val raw = " 42 "
val cleaned = raw.trim()
println("raw='$raw'") // raw=' 42 '
println("cleaned='$cleaned'") // cleaned='42'
}
Обратите внимание на приём с кавычками: когда мы печатаем строку как '$raw', пробелы становятся видимыми, иначе вы просто увидите «42» и будете думать, что пробелов нет (а они там тихо сидят и улыбаются).
trim() не волшебник: что он не делает
У новичков есть естественная мечта: «я сейчас сделаю trim() и строка станет идеальной». Но trim() — не генеральная уборка квартиры, а только «подмели у порога». Он не удаляет пробелы внутри строки, потому что иногда эти пробелы — часть смысла: "Hello world" не должен превращаться в "Helloworld".
Это очень важно, иначе вы можете случайно испортить данные. Например, имя "Ada Lovelace" с пробелом внутри — это нормальное имя, и убрать пробел означает сломать реальность (а реальность и так хрупкая).
Посмотрим на пример, где пробел внутри остаётся:
fun main() {
val raw = " 10 20 "
val cleaned = raw.trim()
println("raw='$raw'") // raw=' 10 20 '
println("cleaned='$cleaned'") // cleaned='10 20'
}
Видите: края очистились, середина осталась. И это правильно.
6. Сценарии ввода/вывода
Сценарий 1: «Вежливое знакомство»
Сделаем самый дружелюбный сценарий: программа спрашивает имя и возраст (возраст пока можно оставить текстом, чтобы не усложнять), а потом печатает сообщение.
Здесь важна привычка: каждый ввод — отдельная переменная. Не пытайтесь «читать два раза и забыть», иначе вы сами потеряете нить разговора.
fun main() {
print("Как вас зовут? ")
val name = readln().trim()
print("Сколько вам лет (можно текстом)? ")
val ageText = readln().trim()
println("Привет, $name! Вам '$ageText' — звучит солидно.")
// например: Привет, Ada! Вам '20' — звучит солидно.
}
Почему trim() здесь полезен даже для имени? Потому что люди иногда вводят случайный пробел в конце (особенно на телефоне), и тогда имя будет "Ada " — и это будет выглядеть странно в выводе.
Сценарий 2: «Мини-калькулятор»
Соберём маленькую программу, которая просит два целых числа и печатает результаты нескольких операций. Здесь есть тонкость: если мы используем toInt(), программа может упасть на плохом вводе; если используем toIntOrNull(), получим null, но не сможем нормально посчитать без дополнительных тем (ветвления, обработка ошибок). Поэтому сделаем два варианта: «строгий» и «наблюдательный».
Строгий вариант
Здесь мы делаем ставку на дисциплину пользователя. Это нормально для учебного примера: вы увидите, как выглядит «счастливый путь».
fun main() {
print("a: ")
val a = readln().trim().toInt()
print("b: ")
val b = readln().trim().toInt()
println("a + b = ${a + b}") // например: a + b = 13
println("a - b = ${a - b}")
println("a * b = ${a * b}")
}
Важная ремарка: деление / и остаток % мы технически умеем, но деление на ноль «ломает» выполнение. Пока что мы не умеем красиво защищаться от этого без будущих инструментов, поэтому в сегодняшней лекции лучше не провоцировать a / b в интерактивном вводе.
Наблюдательный вариант
Теперь сделаем вариант, который не падает на плохом вводе и помогает понять, что реально получилось из ввода.
fun main() {
print("Введите a: ")
val aText = readln()
val a: Int? = aText.trim().toIntOrNull()
print("Введите b: ")
val bText = readln()
val b: Int? = bText.trim().toIntOrNull()
println("aText='$aText' -> a=$a") // например: aText=' 7 ' -> a=7
println("bText='$bText' -> b=$b") // например: bText='q' -> b=null
}
Это выглядит «скучно», потому что мы не считаем. Но педагогически это честный шаг: вы учитесь строить ввод так, чтобы программа была предсказуемой и не превращалась в лотерею «упадёт / не упадёт».
Сценарий 3: «Касса в столовой»
Соберём мини-приложение, которое уже похоже на реальную задачу: есть товар (текст), есть цена и количество (числа), и нужно вывести сумму.
Чтобы не упираться в обработку ошибок, возьмём строгий путь (toInt()), но будем дисциплинированными с trim() и аккуратными с выводом через templates.
fun main() {
print("Название товара: ")
val title = readln().trim()
print("Цена (целое число): ")
val price = readln().trim().toInt()
print("Количество (целое число): ")
val qty = readln().trim().toInt()
val total = price * qty
println("Товар: $title") // например: Товар: Булочка
println("Итого: $qty * $price = $total") // Итого: 2 * 35 = 70
}
Здесь вы используете почти всё, что было раньше:
- строка приходит через readln();
- мы нормализуем её через trim();
- числа получаем через toInt();
- считаем на Int (*);
- выводим через templates ${...}.
7. Отладка ввода и шпаргалка
Наблюдаемый ввод: пробелы, кавычки и длина строки
Иногда программа «не работает», но на самом деле вы просто не видите, что именно пришло во вводе. Пробелы — чемпионы по скрытности: они есть, но их не видно. Поэтому в учебных примерах полезно печатать строки в кавычках и иногда показывать length.
fun main() {
print("Введите что-нибудь (я покажу длину): ")
val raw = readln()
val cleaned = raw.trim()
println("raw='$raw' (len=${raw.length})")
println("cleaned='$cleaned' (len=${cleaned.length})")
}
Если вы введёте три пробела и Enter, то raw.length будет 3, а cleaned.length станет 0. Это прямое подтверждение того, что trim() убирает пробелы по краям.
Шпаргалка по функциям
Иногда полезно собрать «карточку» по инструментам, чтобы мозг не держал всё в тумане.
| Инструмент | Что делает | Что возвращает |
|---|---|---|
|
печатает без перевода строки (удобно для подсказки) | |
|
печатает с переводом строки | |
|
читает одну́дну строку до Enter | |
|
убирает пробелы по краям строки | |
|
строго парсит строку как Int | (или ошибка при запуске) |
|
пытается парсить строку как Int | (число или null) |
|
вставляет значения в текст (templates) | |
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ