Sekarang kita mengarahkan perangkat stream yang sudah dikuasai ke tujuan yang paling penting: file di disk. Header <fstream> menyediakan tiga kelas yang menempel persis pada peta dari Chapter Peta iostream — std::ofstream untuk menulis, std::ifstream untuk membaca, dan std::fstream untuk keduanya. Karena ketiganya turunan ostream/istream, seluruh operator <</>>, manipulator, dan pola yang kita latih berlaku tanpa penyesuaian. Yang benar-benar baru hanyalah dua hal: cara membuka file beserta mode-nya, dan jaminan penutupan otomatis lewat RAII.

Tiga Kelas File Stream #

KelasArahMode bawaanSetara stream lain
std::ofstreamProgram → fileios::out | ios::truncSeperti cout, tujuannya file
std::ifstreamFile → programios::inSeperti cin, sumbernya file
std::fstreamDua arahios::in | ios::outGabungan keduanya

Sebuah ofstream bukan “perintah tulis file” melainkan objek stream yang bertahan — ia membuka file saat dikonstruksi (atau saat open() dipanggil), menjaga file tetap terhubung selama hidup, lalu menutupnya saat objek hancur. Pola hidup-mati objek ini yang membuat penanganan file di C++ terasa rapi dibanding membuka-menutup manual.

Program berikut melakukan siklus lengkap dalam satu file: menulis dua baris ke file baru, menambah satu baris dengan mode append, lalu membaca seluruh isinya kembali:

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

int main() {
    // 1. Tulis file baru (mode bawaan: ios::out | ios::trunc)
    {
        std::ofstream tulis("catatan.txt");
        if (!tulis.is_open()) {
            std::cerr << "Gagal membuka catatan.txt untuk ditulis\n";
            return 1;
        }
        tulis << "Belajar fstream hari ini\n";
        tulis << "RAII menutup file otomatis\n";
    }   // destructor ofstream: file tertutup dan ter-flush

    // 2. Tambah satu baris (mode app = append)
    {
        std::ofstream tambah("catatan.txt", std::ios::app);
        if (!tambah.is_open()) {
            std::cerr << "Gagal membuka file untuk append\n";
            return 1;
        }
        tambah << "Baris tambahan lewat mode app\n";
    }

    // 3. Baca seluruh isi kembali
    std::ifstream baca("catatan.txt");
    if (!baca.is_open()) {
        std::cerr << "Gagal membuka catatan.txt untuk dibaca\n";
        return 1;
    }
    std::cout << "Isi catatan.txt:\n";
    std::string baris;
    while (std::getline(baca, baris)) {
        std::cout << "  " << baris << '\n';
    }
    return 0;
}

Output yang Dihasilkan:

text
Isi catatan.txt:
  Belajar fstream hari ini
  RAII menutup file otomatis
  Baris tambahan lewat mode app
  1. Baris 8-9 (blok kurawal + konstruksi): std::ofstream tulis("catatan.txt") langsung membuka file. Blok {} sengaja dibuat supaya keberadaan objek ini tidak melebihi kebutuhannya.
  2. Baris 10-12 (is_open()): pembukaan bisa gagal — folder tidak ada, izin kurang. Hasilnya bukan program mati melainkan state gagal, dan is_open() adalah pemeriksa pertama yang wajib.
  3. Baris 15-16 (tulis << ...): penulisan terasa sama persis dengan cout; karakternya diantrikan di buffer stream.
  4. Baris 18 (akhir blok): tulis hancur di sini, dan destructor-nya menutup file sekaligus mengosongkan buffer. Tanpa satu baris close() pun, data sudah aman di disk — inilah RAII yang dipelajari pada Section Pemrograman Berorientasi Objek bekerja pada resource nyata.
  5. Baris 21-22 (mode app): pembukaan kedua memakai std::ios::app, sehingga file yang sama tidak di-trunc; baris baru ditambahkan di ujung.
  6. Baris 30-31 (getline loop): pembacaan kembali memakai pola yang akan kita bedah tuntas pada Chapter berikutnya — selama getline berhasil, satu baris masuk ke baris.

Mode Pembukaan #

Parameter kedua konstruktor (dan open()) menerima satu atau beberapa mode yang digabung dengan |:

ModeArti
ios::inBuka untuk membaca; posisi awal di awal file
ios::outBuka untuk menulis
ios::appSemua tulisan selalu di ujung file (append)
ios::truncBuang isi lama — file jadi kosong saat dibuka
ios::binaryAkses per byte tanpa terjemahan karakter khusus

Mode bawaan sudah disesuaikan per kelas: ifstream memakai in, ofstream memakai out | trunc, dan fstream memakai in | out. Kombinasi lazim yang layak dihafal: std::ios::app untuk log yang terus bertambah, std::ios::out | std::ios::app bila ingin eksplisit, dan std::ios::in | std::ios::out untuk membaca-lalu-memperbarui tanpa menghilangkan isi. Perbedaan app dan out | app detailnya halus; untuk pemula, gunakan app saja saat maksudnya menambah.

is_open dan Jaminan RAII #

is_open() mengembalikan true hanya bila file benar-benar terhubung dengan stream. Kegagalan membuka juga menyalakan failbit, sehingga if (!tulis) sama informatifnya dengan if (!tulis.is_open()) — pilih salah satu gaya dan konsisten. Pemeriksaan di atas punya alasan penting: std::fstream secara bawaan tidak melempar exception saat gagal membuka, jadi pemeriksaan manual adalah satu-satunya alarm.

Sedikit tentang penutupan: kita bisa memanggil out.close() eksplisit, dan itu perlu dalam kasus jarang — misalnya ingin memakai ulang objek yang sama untuk file lain, atau memastikan file tertutup sebelum mengganti namanya. Di luar itu, biarkan RAII bekerja: file otomatis tertutup tepat ketika objek keluar dari scope, urutannya terbalik dengan pembukaan bila ada banyak stream terbuka, dan tidak ada jalur eksekusi — termasuk return di tengah fungsi atau exception — yang bisa lolos melewatinya.

Fondasi file kita sudah berdiri: buka, tulis, tambah, baca, tutup. Dua chapter berikutnya memperdalam dua sisi siklus ini — membaca file teks dengan pola yang benar, lalu menulisnya dengan aman.

Referensi #