Input dari pengguna adalah satu-satunya bagian program yang tidak bisa kita kendalikan penuh — kita meminta angka, tetapi yang diketik bisa saja abc, angka di luar rentang, atau campuran keduanya. Kabar baiknya, std::cin tidak menyerah saat ekstraksi gagal: ia mencatat kegagalan pada state stream, dan kita bisa memulihkannya secara terkendali dengan cin.clear() dan cin.ignore(). Chapter ini membedah apa yang sebenarnya terjadi di dalam stream saat parsing gagal, membangun pola validasi loop-sampai-valid yang tangguh, dan membongkar perilaku num_get — mekanisme internal yang menentukan seberapa jauh cin membaca angka.
State Stream dan Flag Galat #
Setiap stream mengelola tiga flag galat yang bersama-sama membentuk state-nya. Kondisi normal disebut goodbit — nilainya nol, artinya tidak ada flag aktif.
| Flag | Arti | Contoh pemicu |
|---|---|---|
eofbit | Sudah mencapai akhir input | Input berakhir saat pembacaan |
failbit | Ekstraksi gagal, data tidak sah | cin >> int menerima "abc" |
badbit | Galat serius, stream tidak bisa dipakai | Kegagalan I/O di level bawah |
Saat std::cin >> umur bertemu teks yang bukan angka, dua hal terjadi sekaligus. Pertama, failbit menyala dan seluruh ekstraksi berikutnya langsung gagal tanpa mencoba membaca — stream terkunci sampai di-clear. Kedua, sejak C++11, variabel tujuan ditulis 0, sehingga umur bernilai 0 meskipun pengguna tidak pernah mengetik nol. Karakter yang tidak terbaca tidak dibuang: ia tetap menunggu di buffer dan akan mengganggu pembacaan berikutnya.
Memulihkan Stream: clear dan ignore #
Kombinasi pemulihan standar terdiri dari dua langkah yang saling melengkapi. std::cin.clear() mengembalikan semua flag ke goodbit sehingga stream bisa dipakai lagi — tetapi ia tidak menyentuh buffer. std::cin.ignore(n, '\n') melanjutkan pekerjaannya: membaca dan membuang karakter maksimal n buah, atau sampai menemukan '\n' (yang ikut dibuang). Argumen pertama lazim diisi idiom std::numeric_limits<std::streamsize>::max() — nilai terbesar yang mungkin — sehingga praktis “buang sampai akhir baris”.
std::cin.clear(); // 1. reset flag
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // 2. buang baris
Mengapa clear() saja tidak cukup? Karena sisa input yang rusak tetap di buffer — cin >> umur berikutnya akan gagal lagi, clear() dipanggil lagi, dan program terjebak loop tanpa henti. Pasangan clear-ignore adalah satu paket: reset flag, lalu bersihkan sisa baris.
Pola Validasi: Loop sampai Valid #
Program berikut memaksa pengguna memberikan umur yang sah: harus angka, dan harus di rentang 1-120. Contoh ini diuji dengan input yang sengaja rusak dua kali sebelum berhasil:
#include <iostream>
#include <limits>
int main() {
int umur = 0;
while (true) {
std::cout << "Masukkan umur (1-120): ";
if (std::cin >> umur) { // ekstraksi sukses?
if (umur >= 1 && umur <= 120) {
break; // valid: keluar dari loop
}
std::cout << "Umur harus di rentang 1-120.\n";
} else { // ekstraksi gagal
std::cin.clear(); // reset state galat
std::cin.ignore(
std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "Input bukan angka yang sah.\n";
}
}
std::cout << "Terima kasih, umur Anda " << umur << ".\n";
return 0;
}Output yang Dihasilkan:
Masukkan umur (1-120): Input bukan angka yang sah.
Masukkan umur (1-120): Umur harus di rentang 1-120.
Masukkan umur (1-120): Terima kasih, umur Anda 25.(Bila program dijalankan di terminal sungguhan, ketikan pengguna akan muncul setelah tiap prompt; saat input di-pipe seperti uji di atas, hanya output program yang terlihat.) Bedah mekanismenya:
- Baris 8 (
while (true)): loop disengaja tanpa kondisi keluar — satu-satunya pintu keluar adalahbreaksetelah input valid, sehingga pengguna tidak bisa lolos dengan data rusak. - Baris 10 (
if (std::cin >> umur)): ekspresi ekstraksi mengembalikan stream itu sendiri, dan stream punya konversi kebooldari statenya. Sukses berartitrue; gagal berartifalsedenganfailbitmenyala. - Baris 11-15 (rentang): ekstraksi sukses belum tentu data sah —
300lolos parsing tetapi ditolak pemeriksaan rentang, tanpa perlu clear karena buffer sudah bersih. - Baris 17-21 (pemulihan): jalur gagal menjalankan paket lengkap:
clear()laluignore(). Tanpa keduanya,"abc"akan menggagalkan semua percobaan berikutnya.
Perilaku num_get: Berhenti di Karakter Pertama yang Salah #
Mekanisme yang menerjemahkan karakter menjadi angka bernama num_get. Aturan kerjanya: lewati spasi di awal, baca karakter selama masih sah untuk tipe tujuan, dan berhenti di karakter pertama yang tidak cocok — karakter itu tidak dibuang, melainkan ditinggalkan di buffer. Ini menjelaskan kenapa "25abc" menghasilkan 25, bukan kegagalan:
#include <iostream>
#include <string>
int main() {
int umur = 0;
std::cout << "Ketik angka lalu huruf, misal 25abc: ";
std::cin >> umur; // num_get membaca "25", berhenti di 'a'
if (std::cin) {
std::cout << "Berhasil membaca: " << umur << '\n';
std::string sisa;
std::getline(std::cin, sisa); // ambil sisa baris yang belum dibaca
if (!sisa.empty()) {
std::cout << "Sisa di buffer: '" << sisa << "'\n";
}
} else {
std::cout << "Ekstraksi gagal total.\n";
}
return 0;
}Output yang Dihasilkan:
Ketik angka lalu huruf, misal 25abc: Berhasil membaca: 25
Sisa di buffer: 'abc'- Baris 9 (
std::cin >> umur):num_getmengonsumsi25dan berhenti dia; state tetapgoodbitkarena angka berhasil terbaca. - Baris 11 (
if (std::cin)): pemeriksaan state menegaskan keberhasilan — inilah bedanya dengan kasus"abc"penuh yang menggagalkan seluruh ekstraksi. - Baris 13-16 (
std::getline): sisa barisabcmasih memenuhi buffer;getlinemenyedotnya sehingga kita bisa melihat bukti parsing parsial.
Parsial ini bukan sekadar trivia: 25kg untuk input berat badan akan diam-diam tercatat 25 tanpa keluhan — kadang memang diinginkan, kadang jadi sumber data salah. Bila ingin tahu apakah ada sisa, ikuti pola di atas; bila ingin menolak sisa, baca satu baris utuh dengan getline lalu parse string-nya — teknik yang kita pelajari pada Chapter stringstream. Alternatif lain adalah std::stoi, yang mengembalikan posisi karakter terakhir yang terpakai melalui parameter keluaran.
Dengan pola clear-ignore dan loop validasi, cin kini aman kita bawa ke program sungguhan. Chapter berikutnya membawa teknik parsing yang sama — operator >>, getline, parsing angka — ke medan baru: memproses teks yang hidup di memori lewat std::stringstream.