Menulis file terasa sederhana — buka, <<, selesai — tetapi justru di sisi tulis inilah kesalahan yang paling mahal terjadi: satu pembukaan dengan mode yang salah bisa menghapus berkas penjualan setahun. Chapter ini membahas penulisan file teks secara lengkap: perbedaan trunc dan app, cara memeriksa bahwa penulisan benar-benar berhasil, kapan data baru turun ke disk lewat flush, kebiasaan penamaan file yang sehat, dan pola pengaman agar program tidak menimpa data penting. Contoh utamanya membangun data.csv — format yang akan terus kita pakai sampai akhir section ini.

trunc versus app: Dua Sikap terhadap Isi Lama #

Ketika std::ofstream dibuka, sistem menentukan nasib isi lama berdasarkan mode. Dua mode itulah yang menentukan apakah program Anda “membuat dokumen baru” atau “menambah catatan”:

ModeIsi lamaPosisi tulisPemakaian lazim
bawaan (out | trunc)Dihapus seketikaAwal fileMembuat laporan baru, export data
ios::appDipertahankanSelalu ujung fileLog, catatan yang terus bertambah
ios::out tanpa truncTetap ada, ditulis di awal menimpa dari depanAwal filePerbaikan file di posisi tertentu (jarang)

Perhatikan baris ketiga: ios::out saja tidak menghapus isi lama, tetapi penulisan dimulai dari depan sehingga byte baru menimpa byte lama secara langsung — perilaku yang jarang diinginkan dan mudah menghasilkan file campur aduk. Untuk kebutuhan harian kita cukup mengingat dua pilihan: default (kosongkan dan tulis dari awal) atau app (tambah di ujung).

Membangun data.csv Langkah demi Langkah #

Program berikut melakukan empat hal: membuat data.csv baru, memeriksa keberhasilan tulis, menambah satu baris lewat mode app, lalu membaca kembali seluruh isinya sebagai bukti:

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

int main() {
    // 1. Buat file baru: mode bawaan ios::out | ios::trunc
    std::ofstream csv("data.csv");
    if (!csv) {
        std::cerr << "Gagal membuat data.csv\n";
        return 1;
    }

    csv << "produk,harga,stok\n";
    csv << "Kopi Toraja,45000,12\n";
    csv << "Teh Hijau,18500,105\n";
    csv << "Gula Pasir,15500,7\n";
    csv.flush();                     // paksa isi buffer turun ke disk
    if (!csv) {                      // periksa hasil penulisan
        std::cerr << "Penulisan data.csv mengalami galat\n";
        return 1;
    }
    std::cout << "data.csv dibuat.\n";

    // 2. Tambahkan satu baris dengan mode app (isi lama tetap aman)
    {
        std::ofstream tambah("data.csv", std::ios::app);
        if (!tambah) {
            std::cerr << "Gagal membuka data.csv untuk append\n";
            return 1;
        }
        tambah << "Sirup Markisa,21000,30\n";
    }   // tambah ditutup di sini: buffer ter-flush sebelum file dibaca

    // 3. Buka file di folder yang tidak ada -> pembukaan gagal
    std::ofstream rusak("folder-tidak-ada/data.csv");
    if (!rusak) {
        std::cerr << "folder-tidak-ada/data.csv tidak bisa dibuka\n";
    }

    // 4. Baca dan tampilkan isi akhir
    std::ifstream baca("data.csv");
    std::string baris;
    std::cout << "--- isi data.csv ---\n";
    while (std::getline(baca, baris)) {
        std::cout << baris << '\n';
    }
    return 0;
}

Output yang Dihasilkan:

text
data.csv dibuat.
--- isi data.csv ---
produk,harga,stok
Kopi Toraja,45000,12
Teh Hijau,18500,105
Gula Pasir,15500,7
Sirup Markisa,21000,30

Pesan folder-tidak-ada/data.csv tidak bisa dibuka tidak muncul di blok di atas karena dikirim lewat std::cerr — sesuai kesepakatan dari Section Dasar Pemrograman bahwa galat mengalir ke stderr. Mari kita bedah programnya:

  1. Baris 8-11 (buka + periksa): mode bawaan out | trunc berarti bila data.csv sudah ada, isinya langsung kosong pada saat file berhasil dibuka — sebelum baris pertama ditulis.
  2. Baris 13-16 (tulis baris): setiap << mengirim satu baris CSV; formatnya bebas rapi karena kita yang menentukan struktur koma-nya.
  3. Baris 17-21 (flush + cek): flush() memaksa isi buffer turun ke disk seketika; pemeriksaan if (!csv) setelahnya mengetes state stream — bila disk penuh atau galat I/O terjadi, program berhenti dengan pesan, bukan diam-diam menganggap semua beres.
  4. Baris 24-32 (append dalam scope): file dibuka ulang dengan ios::app. Blok kurawal sengaja dipasang supaya tambah tertutup dan buffer-nya ter-flush sebelum bagian pembacaan dimulai — tanpa penutupan ini, baris Sirup Markisa masih menunggu di memori dan tidak ikut tampil.
  5. Baris 35-38 (galat pembukaan): folder folder-tidak-ada tidak ada, jadi rusak gagal terbuka; stream masuk state gagal dan program melanjutkan dengan pesan — tidak ada exception, tidak ada crash.
  6. Baris 41-45 (verifikasi): membaca kembali file yang baru ditulis adalah kebiasaan baik saat belajar I/O: hasil akhirnya terlihat nyata, bukan diasumsikan.

Memastikan Data Sampai ke Disk #

Aliran data dari << sampai disk melewati dua tempat: buffer stream di memori, lalu sistem operasi. Operator << hanya menulis ke buffer; pemindahan ke disk terjadi saat buffer penuh, saat flush() dipanggil, atau saat stream ditutup (termasuk oleh destructor). Untuk file biasa, penutupan otomatis RAII sudah cukup — semua data pasti sampai sebelum program berakhir. flush() eksplisit kita perlukan pada dua situasi: ketika proses lain perlu membaca file sebelum stream kita ditutup (seperti pola pada program di atas), dan ketika kita ingin data aman lebih awal sebelum bagian program yang berisiko dijalankan.

Perlu jujur soal batasnya: flush menjamin data berpindah dari buffer stream ke sistem operasi, bukan selesai tertulis fisik di medium. Untuk pencatatan transaksi yang benar-benar kritis, C++ menyediakan sync() dan sistem operasi menyediakan mekanisme fsync — topik yang lebih dekat ke pemrograman sistem, dan cukup kita ketahui ada. Untuk kebutuhan aplikasi umum, pemeriksaan state stream setelah tulis seperti baris 18 di atas adalah level jaga yang memadai.

Penamaan File dan Prinsip Jangan Menimpa #

Nama file adalah kontrak kecil dengan pengguna dan alat lain. Beberapa kebiasaan yang terbukti menyelamatkan: gunakan nama deskriptif (penjualan-2026.csv lebih informatif daripada data2.csv), pilih ekstensi yang sesuai isi (.txt, .csv, .log), hindari spasi dan huruf kapital, dan jangan taruh data program yang bisa ditimpa pengguna di folder berbeda dengan file sumber.

Pengamanan data penting berdiri di atas satu pertanyaan: apakah menimpa file ini disengaja? Tiga pola pengaman yang umum:

cpp Pola pengaman sebelum menulis
// 1. Periksa keberadaan dulu (gagal diam bila file tidak ada)
std::ifstream cek("data.csv");
if (cek.good()) { /* file sudah ada: minta konfirmasi atau batalkan */ }

// 2. Nama berbeda per proses: laporan-2026-10-17.csv, bukan laporan.csv

// 3. Tulis ke file sementara, sukses baru ganti nama:
//    data.csv.tmp -> data.csv (rename dibahas pada Chapter filesystem)

Sampai di sini kita menguasai siklus teks penuh: tulis dengan mode yang benar, pastikan sampai disk, jangan merusak data lama. Sisa section ini menyusuri dua penurunan tingkat lagi — file biner yang berbicara dalam byte, dan pustaka <filesystem> yang mengelola file sebagai objek sistem berkas.

Referensi #