Membaca file adalah kebalikan dari menulis, tetapi punya satu jebakan yang nyaris setiap pemula C++ pernah alami: loop yang memproses baris terakhir dua kali karena pemeriksaan akhir file yang kurang tepat. Pada Chapter ini kita membangun pola baca yang benar sejak awal — while (std::getline(file, baris)) untuk membaca per baris dan while (file >> a >> b) untuk membaca per token — lalu membongkar mengapa while (!file.eof()) yang tampak masuk akal justru salah. Agar contoh berdiri sendiri, setiap program membuat file datanya sendiri terlebih dahulu, memakai cara dari Chapter File Streams.
Membaca per Baris dengan getline #
Fungsi std::getline(stream, baris) membaca karakter sampai bertemu '\n', menyimpannya ke std::string tanpa menyertakan newline, lalu mengkonsumsi newline itu. Nilai kembaliannya adalah stream itu sendiri, sehingga bisa langsung dijadikan kondisi loop — persis seperti if (std::cin >> umur) pada Chapter Input Validation:
#include <fstream>
#include <iostream>
#include <string>
int main() {
// Siapkan file data agar contoh berdiri sendiri
{
std::ofstream siap("resep.txt");
siap << "Nasi goreng spesial\n";
siap << "Bahan: nasi, telur, bawang\n";
siap << "Tumis bawang, masukkan nasi\n";
siap << "Sajikan hangat\n";
}
std::ifstream file("resep.txt");
if (!file) {
std::cerr << "resep.txt tidak bisa dibuka\n";
return 1;
}
std::string baris;
int nomor = 1;
while (std::getline(file, baris)) {
std::cout << nomor << " | " << baris << '\n';
++nomor;
}
std::cout << "Total " << nomor - 1 << " baris dibaca\n";
return 0;
}Output yang Dihasilkan:
1 | Nasi goreng spesial
2 | Bahan: nasi, telur, bawang
3 | Tumis bawang, masukkan nasi
4 | Sajikan hangat
Total 4 baris dibaca- Baris 8-14 (persiapan): blok scope menulis file
resep.txtdengan empat baris, lalu menutupnya otomatis saat blok berakhir — pola write-then-read yang membuat contoh bisa dijalankan di mana saja. - Baris 16-20 (buka dan periksa):
std::ifstreammembuka file untuk dibaca; bila file tidak ada atau tak terbaca, state gagal dan pesan dikirim lewatcerr. - Baris 24-27 (loop utama): setiap iterasi
getlinemengambil satu baris utuh (spasi ikut masuk, newline dibuang). Loop berjalan selama ekstraksi berhasil — tidak ada pemeriksaaneofeksplisit sama sekali. - Baris 28 (hitungan): karena
nomorbertambah setelah tiap keberhasilan,nomor - 1saat loop usai adalah jumlah baris yang benar-benar terbaca.
Jebakan eof: Mengapa while (!file.eof()) Salah #
Kesalahan klasiknya tampak seperti ini:
while (!file.eof()) { // cek dulu, baca kemudian
std::getline(file, baris);
std::cout << baris << '\n'; // baris terakhir dicetak dua kali
}Akar masalahnya adalah kapan flag eofbit menyala: bukan saat posisi baca tiba di akhir file, melainkan saat percobaan baca melewati akhir file dan mendapati tidak ada lagi yang bisa diambil. Mari kita telusuri dengan file empat baris: setelah baris keempat terbaca, eof masih false (kita belum mencoba melewati ujungnya), jadi loop mengulang, getline gagal, baris tidak berubah isinya — dan baris keempat itu tercetak lagi. Pola yang benar membalik urutannya: baca dulu, cek keberhasilan kemudian.
while (std::getline(file, baris)) { // baca dulu
std::cout << baris << '\n'; // proses hanya bila sukses
}Aturan yang bisa kita pegang: jangan pernah membaca data stream hanya karena “belum eof”; selalu jadikan hasil operasi baca sebagai kondisi loop. Flag eof() tetap berguna setelah loop — misalnya membedakan “berhenti karena benar-benar habis” dengan “berhenti karena galat format” — tetapi tidak pernah sebagai kondisi utama.
Membaca per Token dengan operator» #
Sering kali isi file bukan kalimat, melainkan data terstruktur: deretan angka yang dipisah spasi atau newline. Untuk bentuk seperti ini, operator>> lebih tepat karena memotong input pada spasi apa pun dan mengonversi langsung ke tipe tujuan:
#include <fstream>
#include <iomanip>
#include <iostream>
int main() {
// Siapkan file data agar contoh berdiri sendiri
{
std::ofstream siap("pengukuran.txt");
siap << "12.5 8 30\n";
siap << "9.5 10 40\n";
siap << "10.0 7 25\n";
}
std::ifstream file("pengukuran.txt");
if (!file) {
std::cerr << "pengukuran.txt tidak bisa dibuka\n";
return 1;
}
std::cout << std::fixed << std::setprecision(1);
double suhu = 0.0;
int kelembapan = 0;
int durasi = 0;
int catatan = 0;
double totalSuhu = 0.0;
while (file >> suhu >> kelembapan >> durasi) {
totalSuhu += suhu;
++catatan;
std::cout << "Suhu " << suhu << " C, kelembapan " << kelembapan
<< "%, durasi " << durasi << " menit\n";
}
if (catatan > 0) {
std::cout << "Rata-rata suhu: " << totalSuhu / catatan << " C\n";
}
return 0;
}Output yang Dihasilkan:
Suhu 12.5 C, kelembapan 8%, durasi 30 menit
Suhu 9.5 C, kelembapan 10%, durasi 40 menit
Suhu 10.0 C, kelembapan 7%, durasi 25 menit
Rata-rata suhu: 10.7 C- Baris 23 (
while (file >> suhu >> kelembapan >> durasi)): satu ekstraksi mengambil tiga nilai berurutan tanpa peduli pemisahnya spasi atau newline — dua baris file, sembilan token, diproses tiga kelompok. Loop berhenti saat salah satu ekstraksi gagal (termasuk saat data habis). - Baris 24-26: nilai langsung dipakai untuk akumulasi; konversi
double/intsudah dilakukannum_get, lengkap dengan konsekuensi parsing parsial yang kita bahas pada Chapter Input Validation. - Baris 30-32 (pembandingan akhir): bila data penting, periksa juga mengapa loop berhenti —
file.eof()benar berarti data habis secara wajar;failbittanpaeofberarti ada token rusak di tengah file.
Memilih getline atau operator» #
| Aspek | std::getline | operator>> |
|---|---|---|
| Satuan baca | Satu baris (sampai '\n') | Satu token (sampai spasi apa pun) |
| Spasi dalam data | Ikut terbaca | Menjadi pemisah |
| Hasil | Selalu std::string | Dikonversi ke tipe tujuan |
| Cocok untuk | Kalimat, catatan, CSV per baris | Angka, tabel data, kata tunggal |
Dua catatan praktis menutup pemilihan ini. Pertama, mencampur keduanya dalam satu file berisiko jebakan klasik: >> berhenti sebelum newline, lalu getline berikutnya membaca sisa baris kosong itu — pemulihannya sama dengan idiom ignore pada Chapter Input Validation. Kedua, bila barisnya sendiri berisi beberapa nilai (misalnya "Budi 21 172.5"), pola terbaik adalah getline per baris lalu menyalurkannya ke istringstream — kombinasi yang sudah kita latih pada Chapter stringstream dan akan terus dipakai di sisa section ini.
Dengan pola baca yang benar di tangan, kita berbalik ke sisi penulisan — dan ke keputusan yang lebih berbahaya: kapan file lama boleh ditimpa, kapan harus ditambah, dan bagaimana memastikan data benar-benar sampai ke disk.