Membaca Input

Membaca input dari terminal dengan readln dan disiplin konversi aman dengan toIntOrNull.

Daftar isi

Program yang nilainya tertulis di dalam kode hanya bisa diuji dengan nilai tersebut, sedangkan program yang berguna menerima masukan dari pengguna terminal. Dengan masukan, satu program yang sama dapat dipakai untuk banyak data, misalnya menghitung rata-rata untuk nilai ujian mana pun yang diketik. Kotlin menyediakan readln dan readlnOrNull untuk membaca satu baris dari standard input, serta toIntOrNull untuk mengubah teks menjadi bilangan bulat secara aman. Setiap contoh dijalankan dengan masukan yang dikirim melalui pipa, sehingga keluarannya dapat direproduksi persis.

Membaca satu baris dengan readln#

Fungsi readln() membaca satu baris masukan dan mengembalikan isinya tanpa pemisah baris di akhir. Buat berkas sapa.kt berikut.

kotlin sapa.kt
fun main() {
    print("Nama: ")
    val nama = readln()
    println("Halo, $nama!")
}

Kompilasi programnya terlebih dahulu, karena langkah kompilasi cukup dilakukan satu kali.

bash
# kompilasi sapa.kt dengan pustaka standar tersemat
kotlinc sapa.kt -include-runtime -d sapa.jar

Kemudian jalankan programnya dengan mengirim satu baris teks melalui pipa. Perintah echo "Ali" mencetak Ali ke standard input program, seolah-olah teks itu diketik pengguna lalu ditekan Enter.

bash
# kirim satu baris masukan ke program
echo "Ali" | java -jar sapa.jar
text Keluaran
Nama: Halo, Ali!

Keluarannya menyambung dalam satu baris karena print("Nama: ") tidak memindahkan baris, sehingga sapaan langsung mengikuti prompt. Pola prompt dengan print dan pembacaan dengan readln() inilah bentuk dasar program interaktif di terminal.

Pembacaan berlangsung sampai tombol Enter ditekan, dan baris kosong pun sah dibaca, yaitu menghasilkan teks kosong tanpa karakter apa pun. Isi yang dikembalikan selalu bertipe String, sehingga masukan angka seperti 42 baru menjadi bilangan setelah dikonversi, sebagaimana dibahas pada bagian konversi.

readlnOrNull dan akhir masukan#

Fungsi readlnOrNull() berperilaku sama dengan readln(), tetapi mengembalikan null alih-alih berhenti ketika masukan sudah habis. Buat berkas baris.kt berikut yang membaca dua baris berurutan.

kotlin baris.kt
fun main() {
    val baris1 = readlnOrNull()
    val baris2 = readlnOrNull()
    println("Baris pertama: $baris1")
    println("Baris kedua: $baris2")
}
bash
# kompilasi baris.kt dengan pustaka standar tersemat
kotlinc baris.kt -include-runtime -d baris.jar
# kirim satu baris saja, lalu masukan habis
echo "satu" | java -jar baris.jar
text Keluaran
Baris pertama: satu
Baris kedua: null

Baris kedua tidak pernah dikirim, sehingga pembacaan kedua menghasilkan null. Nilai null menyatakan ketiadaan nilai, dan program mencetaknya apa adanya pada keluaran. Dengan readlnOrNull(), akhir masukan menjadi keadaan yang dapat diuji oleh program, bukan keadaan yang menghentikannya.

Bila kedua barisnya memang tersedia, pembacaan berurutan mengembalikan keduanya tanpa kejadian apa pun. Jalankan kembali program yang sama dengan mengirim dua baris sekaligus.

bash
# kirim dua baris masukan dalam satu perintah
printf "Ali\nBudi\n" | java -jar baris.jar
text Keluaran
Baris pertama: Ali
Baris kedua: Budi

Urutan pembacaan mengikuti urutan baris yang dikirim, sehingga Ali terbaca pada pembacaan pertama dan Budi pada pembacaan kedua. Pola ini menegaskan bahwa setiap pemanggilan readlnOrNull() mengonsumsi satu baris, dan baris yang sudah dibaca tidak tersedia lagi untuk pembacaan berikutnya.

Konversi aman dengan toIntOrNull#

Masukan yang dibaca selalu bertipe String, sehingga angka yang diketik pengguna perlu dikonversi terlebih dahulu. Fungsi toIntOrNull() mengubah teks menjadi Int bila isinya memang bilangan bulat, dan mengembalikan null bila tidak. Buat berkas konversi.kt berikut.

kotlin konversi.kt
fun main() {
    print("Angka: ")
    val masukan = readln()
    val angka = masukan.toIntOrNull()
    println("Masukan: $masukan")
    println("Hasil konversi: $angka")
}
bash
# kompilasi konversi.kt dengan pustaka standar tersemat
kotlinc konversi.kt -include-runtime -d konversi.jar
# uji dengan masukan angka yang sah
echo "42" | java -jar konversi.jar
text Keluaran
Angka: Masukan: 42
Hasil konversi: 42

Jalankan kembali program yang sama dengan masukan yang bukan angka.

bash
# uji dengan masukan yang tidak sah
echo "abc" | java -jar konversi.jar
text Keluaran
Angka: Masukan: abc
Hasil konversi: null

Program yang sama menghasilkan 42 pada masukan pertama dan null pada masukan kedua, tanpa peringatan ataupun penghentian. Disiplin konversi seperti ini menjaga program tetap hidup di hadapan masukan yang tak terduga, dan nilai null tersebut dapat diuji sebelum dipakai sebagai angka.

Penolakan otomatis ini sejalan dengan sifat tipe pada Kotlin yang dibahas pada modul Tipe Dasar, yaitu perpindahan antartipe selalu ditulis dengan jelas. Perbedaannya, masukan pengguna tidak dapat diperiksa oleh compiler karena isinya baru diketahui saat program berjalan, sehingga pemeriksaannya dilakukan oleh fungsi toIntOrNull() dan hasilnya dilaporkan sebagai null alih-alih menyebabkan program berhenti.

Kesalahan umum#

Kesalahan yang sering terjadi adalah membaca dengan readln() padahal masukannya sudah habis. Jalankan program sapa.jar tanpa mengirim satu baris pun.

bash
# masukan kosong, langsung mencapai akhir masukan
printf "" | java -jar sapa.jar
text Keluaran
Nama: Exception in thread "main" kotlin.io.ReadAfterEOFException: EOF has already been reached
	at kotlin.io.ConsoleKt.readln(Console.kt:152)
	at SapaKt.main(sapa.kt:3)
	at SapaKt.main(sapa.kt)

Prompt sempat tercetak, tetapi pembacaan barisnya gagal karena tidak ada lagi yang bisa dibaca, dan program berhenti dengan exception. Singkatan EOF pada nama exception itu adalah end of file, yaitu keadaan sumber masukan yang sudah tidak punya data lagi. Perbaikannya adalah menggunakan readlnOrNull() sebagaimana dicontohkan pada bagian sebelumnya, lalu menguji hasil null sebelum nilai itu dipakai.

Ringkasan#

  • readln() mengembalikan isi satu baris masukan, sedangkan readlnOrNull() mengembalikan null ketika masukan sudah habis.
  • print tanpa pemindah baris membentuk prompt yang menyambung dengan jawaban pada baris yang sama.
  • toIntOrNull() mengubah teks menjadi Int hanya bila isinya bilangan bulat yang sah, dan menghasilkan null untuk masukan lain.
  • Masukan melalui pipa, seperti echo "Ali" | java -jar program.jar, membuat perilaku program dapat diuji ulang dengan keluaran yang persis sama.

Referensi#