Keadaan stream dan error

fail, eof, bad, pemulihan stream, dan perulangan masukan yang tangguh.

Daftar isi

Setiap stream memelihara keadaan internal berupa beberapa bit error yang mencatat hasil operasi terakhirnya. Topik ini membahas tiga bit keadaan utama beserta fungsi pemeriksanya, pemulihan stream yang gagal dengan clear() dan ignore(), serta perancangan perulangan masukan yang tetap berjalan menghadapi data yang rusak. Seluruh contoh memakai std::istringstream dengan masukan tetap, sehingga perilakunya dapat direproduksi tanpa interaksi pengguna.

Tiga bit keadaan stream#

Keadaan stream tersimpan sebagai tiga bit yang disatukan dalam tipe std::ios::iostate. Operator >> dan std::getline mengaktifkan bit yang sesuai dengan kegagalan yang dialami, sehingga bit-bit itu menjadi catatan singkat atas operasi terakhir. Pemeriksaannya dilakukan satu per satu lewat fungsi anggota pada tabel berikut.

BitFungsi pemeriksaMakna
goodbitgood()tidak ada bit yang aktif, stream benar-benar bersih
eofbiteof()operasi terakhir mencapai akhir aliran data
failbitfail()operasi terakhir gagal, misalnya teks tidak sesuai tipe sasaran
badbitbad()stream rusak pada tingkat lebih dalam, misalnya kegagalan I/O yang serius

Konversi stream menjadi bool, yaitu uji if (stream) atau kondisi while (stream >> nilai), bernilai true selama failbit dan badbit tidak aktif. Karena eofbit tidak ikut dihitung, pembacaan yang berhasil persis di akhir data tetap dianggap sah. Sebaliknya good() menuntut tidak ada bit sama sekali, sehingga bernilai false meskipun yang terjadi hanyalah pencapaian akhir berkas.

cpp keadaan-stream.cpp
#include <iostream>
#include <limits>
#include <sstream>
#include <string>

void laporkan(const std::istringstream& stream, const std::string& tahap) {
    std::cout << tahap
              << " good=" << stream.good()
              << " eof=" << stream.eof()
              << " fail=" << stream.fail()
              << " bad=" << stream.bad() << "\n";
}

int main() {
    std::istringstream masuk("42 tujuh 100");
    int angka = 0;

    masuk >> angka;  // berhasil
    std::cout << "terbaca " << angka << "\n";
    laporkan(masuk, "setelah 42:");

    masuk >> angka;  // gagal, "tujuh" bukan bilangan
    std::cout << "terbaca " << angka << "\n";
    laporkan(masuk, "setelah tujuh:");

    // pulihkan stream, lalu buang token yang tidak valid
    masuk.clear();
    masuk.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
    masuk >> angka;  // berhasil
    std::cout << "terbaca " << angka << "\n";
    laporkan(masuk, "setelah 100:");
    return 0;
}
text Keluaran
terbaca 42
setelah 42: good=1 eof=0 fail=0 bad=0
terbaca 0
setelah tujuh: good=0 eof=0 fail=1 bad=0
terbaca 100
setelah 100: good=0 eof=1 fail=0 bad=0

Pembacaan token tujuh mengaktifkan failbit dan meninggalkan variabel angka bernilai 0, sedangkan posisi baca tidak berpindah sehingga token itu masih menempel di stream. Setelah clear() menghapus bit error dan ignore() membuang token yang rusak beserta spasi penutupnya, pembacaan 100 berjalan normal. Bit eofbit yang aktif pada akhir program menunjukkan data yang habis, bukan kegagalan penguraian, karena fail() tetap bernilai false.

Di antara bit-bit itu, failbit dan badbit menandai kegagalan dengan makna yang berbeda. failbit menandai operasi yang gagal secara spesifik, misalnya konversi teks ke angka yang tidak cocok, sedangkan badbit menandai kehilangan integritas stream itu sendiri, misalnya kesalahan perangkat saat membaca berkas. Kegagalan pertama dapat dipulihkan dengan clear() dan ignore(), sedangkan operasi berikutnya pada stream yang mengalami badbit biasanya gagal juga. Pergerakan bit dapat diatur lebih halus melalui setstate(), yang hanya menambahkan bit tanpa menghapus yang sudah aktif, serta clear() yang menerima argumen keadaan untuk menetapkan isinya secara langsung.

Memulihkan stream yang gagal#

Pemulihan terdiri atas dua langkah yang selalu berpasangan. clear() menghapus seluruh bit error dan mengembalikan stream ke keadaan bersih, sedangkan ignore() membuang karakter-karakter penyebab kegagalan agar pembacaan berikutnya mulai dari tempat yang wajar. Tanpa langkah kedua, token yang rusak langsung menggagalkan pembacaan sesudahnya.

Bentuk ignore() yang paling aman menetapkan batas maksimum karakter sekaligus karakter penghentinya. Kombinasi berikut membuang seluruh sisa baris saat ini dan dipakai ketika baris tersebut sudah tidak dapat diselamatkan.

cpp Pola pemulihan: membuang sisa baris yang rusak
// tidak-dijalankan (fragmen pola, bukan program utuh)
masuk.clear();  // hapus semua bit error
masuk.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

Nilai std::numeric_limits<std::streamsize>::max() adalah batas terbesar yang diizinkan tipe penghitung stream, dan header <limits> menyediakannya. Kombinasi ini lebih andal daripada menebak panjang baris secara manual. Untuk kebutuhan yang lebih halus, pembuangan dapat dibatasi. Pada program sebelumnya, ignore() dengan karakter penghenti spasi hanya mengosongkan satu token.

ignore() menerima dua argumen opsional, yaitu jumlah karakter maksimum dan karakter penghenti. Versi tanpa argumen membuang satu karakter saja, sedangkan versi dengan jumlah tetap membuang persis sebanyak itu apabila penghentinya tidak ditemukan. Apabila akhir data dicapai sebelum penghenti ditemukan, ignore() mengaktifkan eofbit setelah membuang sisa datanya, dan jumlah karakter yang benar-benar dibuang dapat diperiksa lewat gcount(). Informasi itu berguna untuk memastikan pemulihan bekerja sebagaimana diperkirakan, misalnya memverifikasi bahwa seluruh baris yang rusak benar-benar terbuang.

Perulangan masukan yang tangguh#

Perulangan masukan yang tangguh memisahkan dua kegagalan yang berbeda sifatnya. Kegagalan std::getline menandakan seluruh sumber telah habis, sedangkan kegagalan penguraian di dalam satu baris hanya menandakan baris itu tidak sah. Pola yang tepat memeriksa keduanya secara terpisah, yaitu baris diambil dengan std::getline, kemudian isi barisnya diurai oleh std::istringstream tersendiri.

cpp loop-tangguh.cpp
#include <iostream>
#include <sstream>
#include <string>

int main() {
    const std::string data =
        "07:00 22.5\n"
        "08:00 rusak\n"
        "09:00 24.8\n";

    std::istringstream masuk(data);
    std::string baris;
    double total = 0.0;
    int jumlah = 0;

    while (std::getline(masuk, baris)) {
        std::istringstream kolom(baris);
        std::string waktu;
        double suhu = 0.0;
        if (kolom >> waktu >> suhu) {
            total += suhu;
            ++jumlah;
            std::cout << waktu << " diterima, suhu " << suhu << "\n";
        } else {
            std::cout << waktu << " ditolak, baris dilewati\n";
        }
    }
    std::cout << "jumlah data valid: " << jumlah << "\n";
    return 0;
}
text Keluaran
07:00 diterima, suhu 22.5
08:00 ditolak, baris dilewati
09:00 diterima, suhu 24.8
jumlah data valid: 2

Baris kedua pada data contoh memuat token rusak yang tidak dapat dikonversi menjadi double, sehingga baris tersebut dilaporkan dan dilewati, sedangkan perulangan utama tetap berjalan sampai akhir. Statistik hanya dihitung dari dua baris yang sah. Pola ini berlaku sama pada std::ifstream untuk berkas nyata, karena perilaku std::getline dan pemeriksaan keadaannya identik.

Ketika data yang diproses besar, baris yang ditolak sebaiknya tetap dapat dilacak posisinya. Penghitung nomor baris yang bertambah pada setiap iterasi std::getline memungkinkan pesan penolakan menyebut letak persisnya, misalnya baris kedua berisi token yang tidak dapat dikonversi. Informasi itu tidak mengubah alur program, tetapi mempersingkat pemeriksaan sumber data yang rusak. Penghitung yang sama juga menjadi dasar pembatasan, misalnya berhenti melaporkan setelah sejumlah kegagalan yang wajar agar log tidak dipenuhi baris yang sama.

Kesalahan umum#

Kekeliruan berikut paling sering muncul saat keadaan stream mulai diperiksa.

  • Memeriksa keberhasilan dengan good(). Fungsi ini bernilai false juga ketika hanya eofbit yang aktif, padahal pembacaan terakhir berhasil. Perbaikannya menguji kondisi stream secara langsung atau memakai fail().
  • Memanggil clear() tanpa membuang karakter yang menyebabkan kegagalan. Posisi baca tidak berpindah saat penguraian gagal, sehingga token yang rusak langsung menggagalkan pembacaan berikutnya. Perbaikannya menyandingkan ignore().
  • Memakai variabel sasaran setelah kegagalan. Nilai variabel berubah menjadi 0 untuk tipe aritmetika sehingga perhitungan menjadi menyesatkan. Perbaikannya memeriksa keberhasilan pembacaan sebelum variabel dipakai.
  • Menganggap kegagalan pertama sebagai akhir data. failbit milik satu baris tidak berarti sumbernya habis. Perbaikannya memisahkan pengambilan baris dari penguraian isinya.
  • Menguji akhir data dengan eof() sebelum membaca. eof() baru bernilai true setelah pembacaan melewati akhir data, sehingga kondisi while (!masuk.eof()) mengeksekusi satu iterasi ekstra dengan pembacaan yang gagal. Perbaikannya menjadikan pembacaan itu sendiri sebagai kondisi, misalnya while (std::getline(masuk, baris)).

Ringkasan#

  • Keadaan stream terdiri atas eofbit, failbit, dan badbit, dan masing-masing diperiksa lewat eof(), fail(), serta bad().
  • Konversi stream ke bool hanya peduli pada failbit dan badbit, sehingga eofbit setelah pembacaan terakhir bukan kegagalan.
  • Pemulihan memakai clear() untuk menghapus bit error dan ignore() untuk membuang karakter yang bermasalah.
  • Perulangan yang tangguh mengambil baris dengan std::getline, mengurainya lewat std::istringstream tersendiri, dan melaporkan baris yang tidak sah.

Referensi#