Sampai chapter sebelumnya, seluruh operasi kita selalu menargetkan satu file dengan nama yang sudah pasti. Padahal pekerjaan nyata seputar file sering melampaui satu nama: membuat folder untuk menyimpan hasil, memeriksa apakah sebuah file ada sebelum membacanya, mengukur ukuran file, menelusuri isi sebuah direktori, lalu membersihkannya lagi. Semua kebutuhan itu dijawab oleh <filesystem>, bagian standar C++ sejak C++17 yang menempatkan fungsinya di namespace std::filesystem. Chapter ini memperkenalkan fondasinya — path, create_directory, exists, file_size, directory_iterator, dan remove — melalui satu program yang mengelola folder kecil dari awal sampai akhir.

Namespace std::filesystem dan Alias fs #

Hampir semua kemampuan library ini berupa free function — fungsi biasa, bukan member dari sebuah objek — yang semuanya tinggal di namespace std::filesystem. Nama itu panjang, dan satu program sering memanggilnya belasan kali, sehingga deklarasi namespace fs = std::filesystem; di dekat awal file sudah menjadi kebiasaan yang lazim di seluruh ekosistem C++. Alias ini bukan namespace baru, hanya nama panggilan: fs::exists dan std::filesystem::exists adalah fungsi yang persis sama.

Konsep sentral yang menghubungkan semua fungsi tersebut adalah std::filesystem::path — sebuah tipe yang merepresentasikan lokasi di sistem berkas, misalnya "arsip" atau "arsip/a.txt". Fungsi ofstream dari Chapter File Streams ternyata juga menerima path langsung sebagai nama file-nya, sehingga dua dunia ini menyambung tanpa konversi manual.

Membuat Direktori dan Memeriksa Keberadaan #

Program berikut menjalankan siklus lengkap: membuat folder arsip, mengisi dua file, memeriksa keberadaan dan ukuran, menelusuri isi folder, lalu menghapus semuanya. Folder dan file dibuat relatif terhadap current working directory — tempat program dijalankan — sehingga contoh ini berjalan di mana saja tanpa menyentuh lokasi lain di disk:

cpp
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

namespace fs = std::filesystem;

void tulisFile(const fs::path& lokasi, const std::string& isi) {
    std::ofstream keluar(lokasi);
    if (!keluar) {
        std::cerr << "Gagal menulis " << lokasi.string() << '\n';
        std::exit(1);
    }
    keluar << isi;
}

int main() {
    // 1. Buat direktori kerja
    fs::path arsip = "arsip";
    if (fs::create_directory(arsip)) {
        std::cout << "Direktori 'arsip' dibuat\n";
    } else {
        std::cout << "Direktori 'arsip' sudah ada\n";
    }

    // 2. Isi dengan dua file berukuran pasti
    tulisFile(arsip / "a.txt", "Isi A\n");
    tulisFile(arsip / "laporan-b.txt", "Laporan B panjang\n");

    // 3. Periksa keberadaan dan ukuran
    std::cout << std::boolalpha;
    std::cout << "arsip ada           : " << fs::exists(arsip) << '\n';
    std::cout << "arsip/a.txt ada     : " << fs::exists(arsip / "a.txt") << '\n';
    std::cout << "Ukuran a.txt        : " << fs::file_size(arsip / "a.txt")
              << " byte\n";
    std::cout << "Ukuran laporan-b.txt: "
              << fs::file_size(arsip / "laporan-b.txt") << " byte\n";

    // 4. Enumerasi isi direktori (urutan OS tidak pasti -> urutkan sendiri)
    std::vector<fs::path> daftar;
    for (const fs::directory_entry& entri : fs::directory_iterator(arsip)) {
        daftar.push_back(entri.path());
    }
    std::sort(daftar.begin(), daftar.end());
    std::cout << "Isi 'arsip' (" << daftar.size() << " file):\n";
    for (const fs::path& p : daftar) {
        std::cout << "  " << p.string() << " (" << fs::file_size(p)
                  << " byte)\n";
    }

    // 5. Bersih-bersih: hapus file satu per satu, lalu direktorinya
    fs::remove(arsip / "a.txt");
    fs::remove(arsip / "laporan-b.txt");
    fs::remove(arsip);
    std::cout << "Setelah dihapus, 'arsip' ada: " << fs::exists(arsip) << '\n';
    return 0;
}

Output yang Dihasilkan:

text
Direktori 'arsip' dibuat
arsip ada           : true
arsip/a.txt ada     : true
Ukuran a.txt        : 6 byte
Ukuran laporan-b.txt: 18 byte
Isi 'arsip' (2 file):
  arsip/a.txt (6 byte)
  arsip/laporan-b.txt (18 byte)
Setelah dihapus, 'arsip' ada: false
  1. Baris 8 (namespace fs = ...): alias yang membuat seluruh pemanggilan berikutnya ringkas. Tanpa alias, baris 22 menjadi std::filesystem::create_directory(...).
  2. Baris 21 (fs::path arsip = "arsip"): sebuah path bisa diisi langsung dari string literal. Karena isinya tanpa awalan /, ini path relatif — dihitung dari current working directory saat program berjalan.
  3. Baris 22-26 (create_directory): mengembalikan true bila direktori benar-benar dibuat dan false bila sudah ada sebelumnya. Karena program kita menghapus folder di ujung, setiap kali dijalankan hasilnya selalu “dibuat” — inilah salah satu kunci output yang konsisten.
  4. Baris 29-30 (operator/): arsip / "a.txt" menyambung path dengan pemisah yang benar untuk platform (/ di Linux, \ di Windows). Selalu gunakan operator ini alih-alih menempel sendiri karakter pemisah.
  5. Baris 33-39 (exists, file_size): exists menjawab bool, dan file_size melaporkan jumlah byte — "Isi A\n" berisi 6 karakter, "Laporan B panjang\n" 18. Perhatikan bahwa file_size menghitung byte di disk, jadi \n ikut dihitung satu.
  6. Baris 42-51 (directory_iterator): range-for di atas iterator direktori menghasilkan satu directory_entry per item; entri.path() mengambil lokasinya. Urutan kunjungan tidak dijamin terurut oleh standar, maka kita menampung ke vector lalu std::sort sebelum mencetak — pola wajib bila output program perlu konsisten.
  7. Baris 54-56 (remove): remove menghapus satu file, atau direktori yang kosong. Karena kedua file dihapus lebih dulu, remove(arsip) berhasil; jika kita mencoba menghapus folder yang masih berisi, pemanggilan ini akan gagal dan — seperti akan kita bedah pada Chapter Error Handling File — versi tanpa std::error_code melempar exception.

Dua poin di atas layak digarisbawahi karena paling sering menjebak pemula. Pertama, create_directory yang mengembalikan false bukan kegagalan — “sudah ada” adalah kondisi normal, dan hanya ada satu kondisi ini yang membuat return value-nya false. Kedua, soal urutan iterasi:

Ukuran File dan Batasannya #

fs::file_size nyaman, tetapi ia membaca ukuran dari metadata sistem berkas, bukan dengan membaca isinya. Konsekuensinya cepat, namun ada dua catatan praktis. Untuk file yang sedang ditulis dan belum di-flush, angka yang dilaporkan bisa tertinggal dari data yang sudah dikirim ke buffer stream — pola amannya adalah menutup ofstream lebih dulu (atau keluar dari bloknya) sebelum mengukur. Dan bila path yang diberikan tidak menunjuk file yang ada, versi file_size tanpa parameter tambahan melempar std::filesystem::filesystem_error; cara menanganinya dengan rapi adalah topik inti Chapter Error Handling File.

Satu lagi: bila butuh menghapus folder beserta seluruh isinya sekaligus, pasangannya adalah fs::remove_all yang mengembalikan jumlah entri yang dihapus. Fungsi ini praktis untuk folder sementara (temp), tetapi tidak punya konfirmasi dua langkah — yang dihapus benar-benar hilang.

Yang Sudah, Yang Selanjutnya #

Kita sekarang punya peralatan dasar mengelola sistem berkas: path sebagai alamat, create_directory untuk menyusun struktur, exists dan file_size untuk inspeksi, directory_iterator untuk penelusuran, dan remove untuk membersihkan. Padahal path sendiri masih kita pakai sepintas sebagai string yang disambung dengan / — ia punya berbagai member function untuk membedah dirinya: parent_path, filename, extension, dan stem. Chapter berikutnya membedah tipe itu tuntas, termasuk iterator rekursif yang menyusuri subfolder sampai ke dalam.

Referensi #