File stream

ifstream, ofstream, fstream, mode open, dan pemeriksaan kegagalan.

Daftar isi

Topik ini membahas pembacaan dan penulisan berkas di C++ melalui tiga kelas file stream, yaitu std::ofstream untuk menulis, std::ifstream untuk membaca, dan std::fstream untuk keduanya. Pembahasan mencakup cara membuka berkas beserta mode pembukaannya (open mode), kebiasaan memeriksa kegagalan sejak awal, dan pola menulis lalu membaca kembali satu berkas dalam satu program. Semua contoh membuat berkasnya sendiri, sehingga keluarannya dapat direproduksi tanpa menyiapkan berkas pendamping.

std::ofstream adalah stream keluaran yang tujuannya berkas. Konstruktor kelas ini langsung membuka berkas yang namanya diberikan, sehingga penulisan dapat dimulai segera dengan operator <<, sama seperti mencetak ke std::cout. Sebelum stream dipakai menulis, keberhasilan pembukaannya wajib diperiksa. Objek std::ofstream dapat diuji langsung dalam kondisi if, dan uji tersebut bernilai false apabila berkas tidak berhasil dibuka.

Program berikut menyimpan catatan sensor suhu ke berkas sensor.log, lalu membaca kembali isinya dengan std::getline. Perhatikan pemanggilan keluar.close() sebelum berkas dibaca. Data yang ditulis lewat << pertama-tama ditampung di buffer, yaitu memori perantara, dan belum tentu sampai ke berkas sebelum stream ditutup atau di-flush.

cpp sensor.cpp
#include <fstream>
#include <iostream>
#include <string>

int main() {
    // tulis catatan sensor suhu ke berkas teks
    std::ofstream keluar("sensor.log");
    if (!keluar) {
        std::cerr << "Error: berkas sensor.log tidak bisa dibuat.\n";
        return 1;
    }
    keluar << "07:00 22.5\n";
    keluar << "08:00 23.1\n";
    keluar << "09:00 24.8\n";
    keluar.close();  // tutup agar semua data benar-benar tertulis

    // baca kembali isi berkas baris demi baris
    std::ifstream masuk("sensor.log");
    if (!masuk) {
        std::cerr << "Error: berkas sensor.log tidak bisa dibuka.\n";
        return 1;
    }
    std::string baris;
    int nomor = 0;
    while (std::getline(masuk, baris)) {
        ++nomor;
        std::cout << nomor << ": " << baris << "\n";
    }
    return 0;
}
text Keluaran
1: 07:00 22.5
2: 08:00 23.1
3: 09:00 24.8

Pemanggilan close() memaksa seluruh isi buffer benar-benar tertulis ke berkas sebelum pembacaan dimulai. Tanpa pemanggilan itu, data baru tersimpan ketika objek keluar mengakhiri masa hidupnya di akhir main, sehingga pembacaan di tengah program dapat menemukan berkas yang masih kosong. Penutupan otomatis pada akhir masa hidup objek adalah penerapan prinsip RAII, sehingga berkas selalu tertutup bahkan apabila fungsi berakhir lebih dahulu melalui return.

Membaca berkas dengan ifstream#

std::ifstream adalah stream masukan yang sumbernya berkas. Isi berkas dapat dibaca dengan operator >> untuk data berformat atau dengan std::getline untuk baris utuh, dan pembacaan baris demi baris menjadi pola paling umum untuk berkas teks. Perulangan while (std::getline(masuk, baris)) berjalan selama masih ada baris dan berhenti secara otomatis di akhir berkas.

Pembukaan berkas yang tidak ada tidak melempar exception. Konstruktor std::ifstream melaporkan kegagalan melalui keadaan stream, sehingga pemeriksaan if (!masuk) setelah deklarasi menjadi kebiasaan yang wajib. Program berikut mencoba membaca arsip yang memang tidak tersedia, lalu menangani kegagalannya dengan percabangan.

cpp gagal-buka.cpp
#include <fstream>
#include <iostream>

int main() {
    std::ifstream masuk("arsip-tidak-ada.txt");
    if (!masuk) {
        std::cerr << "Error: arsip-tidak-ada.txt tidak dapat dibuka.\n";
        return 1;
    }
    // bagian ini tidak pernah dijalankan karena pembukaan selalu gagal
    std::string baris;
    while (std::getline(masuk, baris)) {
        std::cout << baris << "\n";
    }
    return 0;
}
text Keluaran
Error: arsip-tidak-ada.txt tidak dapat dibuka.

Pesan error dilaporkan melalui std::cerr, sedangkan return 1 mengakhiri program dengan kode keluar selain nol agar script shell dapat mendeteksinya. Kegagalan yang disengaja seperti ini berulang secara deterministik, karena berkas arsip-tidak-ada.txt memang tidak pernah dibuat dan pesan yang sama selalu muncul pada setiap eksekusi.

Selain pengujian kondisi, keberhasilan pembukaan dapat diperiksa dengan is_open(), yang bernilai true apabila berkas sudah terhubung ke stream. Kedua cara itu setara untuk kebutuhan umum, dan pengujian kondisi lebih ringkas sehingga lebih sering dipakai. Ketika berkas berhasil dibuka tetapi isinya habis, std::getline langsung gagal dan perulangan tidak dijalankan sama sekali, sedangkan baris kosong di tengah berkas tetap terbaca sebagai string kosong.

Mode open dan std::fstream#

Ketiga kelas menerima argumen kedua berupa mode open, yaitu sekumpulan konstanta dari std::ios yang menentukan cara berkas dibuka. Mode dapat digabung dengan operator |. Tanpa argumen kedua, std::ofstream membuka berkas untuk menulis, std::ifstream untuk membaca, sedangkan std::fstream untuk keduanya pada objek yang sama.

ModeEfek
std::ios::inmembuka berkas untuk dibaca, posisi awal di awal berkas
std::ios::outmembuka berkas untuk ditulis
std::ios::appsetiap penulisan ditempatkan di akhir berkas
std::ios::ateposisi awal dipindah ke akhir berkas, tetapi posisi dapat berpindah lagi
std::ios::truncisi berkas yang sudah ada dihapus sejak awal
std::ios::binarydata dibaca dan ditulis apa adanya, tanpa penanganan karakter khusus

Kombinasi yang paling sering dipakai selain mode bawaan adalah std::ios::app untuk menambah data di akhir berkas tanpa menghapus isinya, misalnya pada log yang terus bertambah. Apabila berkas yang sama juga perlu dibaca, std::fstream menjadi pilihan tepat karena pembacaan dan penulisan dilakukan pada satu objek yang sama.

cpp Contoh: menambah baris pada arsip cadangan
// tidak-dijalankan (fragmen mode open, bukan program utuh)
std::fstream arsip("cadangan.log", std::ios::out | std::ios::app);
arsip << "2026-11-02 10:00 25.3\n";

Mode std::ios::ate berbeda dengan std::ios::app dalam satu hal penting. Mode ate hanya memindahkan posisi awal ke akhir berkas, dan posisi itu tetap boleh berpindah kembali. Mode app mengunci setiap penulisan agar selalu ditempatkan di akhir berkas. Adapun mode std::ios::binary mematikan penanganan karakter khusus dan dibahas lebih lanjut pada topik I/O teks versus biner. Tanpa argumen mode, std::fstream menggunakan gabungan std::ios::in dan std::ios::out, sehingga berkasnya wajib sudah ada karena mode in tidak menciptakan berkas baru.

Kesalahan umum#

Kekeliruan berikut paling sering muncul saat program mulai bekerja dengan berkas.

  • Membaca ulang berkas tanpa menutup stream penulisnya terlebih dahulu. Data masih tertahan di buffer, sehingga pembacaan menemukan berkas yang kosong atau isinya parsial. Perbaikannya memanggil close() sebelum stream pembaca dibuka.
  • Mengabaikan hasil pembukaan. Konstruktor tidak melempar exception, sehingga program tanpa pemeriksaan terus berjalan dengan stream yang tidak terhubung ke berkas apa pun. Perbaikannya menguji kondisi stream segera setelah deklarasi.
  • Memakai std::ofstream biasa untuk menambah data. Setiap pembukaan menghapus isi lama. Perbaikannya menambahkan std::ios::app pada argumen mode pembukaannya.
  • Membaca rekaman biner dengan std::getline. Pembacaan mengikuti karakter baris baru, bukan batas rekaman, sehingga hasilnya tidak terstruktur. Perbaikannya membaca dengan read() sebagaimana dibahas pada topik I/O teks versus biner.

Ringkasan#

  • std::ofstream menulis dan std::ifstream membaca, sedangkan std::fstream menangani keduanya pada satu objek.
  • Keberhasilan pembukaan diperiksa dengan menguji kondisi stream, karena konstruktor melaporkan kegagalan melalui keadaan stream, bukan exception.
  • Penulisan lewat << tertahan di buffer sampai stream ditutup atau di-flush, sehingga pembacaan ulang harus menunggu setelah close().
  • Mode open seperti std::ios::app, std::ios::ate, dan std::ios::binary mengubah perilaku pembukaan, sedangkan std::ios::out saja mengosongkan berkas lama.

Referensi#