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)
println("Вік: " + 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() і акуратно виводити результат за допомогою шаблонів рядків.
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 (*);
- виводимо через шаблони рядків ${...}.
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) |
|
вставляє значення в текст (шаблони рядків) | |
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ