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:
#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:
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- Baris 8 (
namespace fs = ...): alias yang membuat seluruh pemanggilan berikutnya ringkas. Tanpa alias, baris 22 menjadistd::filesystem::create_directory(...). - Baris 21 (
fs::path arsip = "arsip"): sebuahpathbisa diisi langsung dari string literal. Karena isinya tanpa awalan/, ini path relatif — dihitung dari current working directory saat program berjalan. - Baris 22-26 (
create_directory): mengembalikantruebila direktori benar-benar dibuat danfalsebila sudah ada sebelumnya. Karena program kita menghapus folder di ujung, setiap kali dijalankan hasilnya selalu “dibuat” — inilah salah satu kunci output yang konsisten. - 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. - Baris 33-39 (
exists,file_size):existsmenjawabbool, danfile_sizemelaporkan jumlah byte —"Isi A\n"berisi 6 karakter,"Laporan B panjang\n"18. Perhatikan bahwafile_sizemenghitung byte di disk, jadi\nikut dihitung satu. - Baris 42-51 (
directory_iterator): range-for di atas iterator direktori menghasilkan satudirectory_entryper item;entri.path()mengambil lokasinya. Urutan kunjungan tidak dijamin terurut oleh standar, maka kita menampung kevectorlalustd::sortsebelum mencetak — pola wajib bila output program perlu konsisten. - Baris 54-56 (
remove):removemenghapus 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 tanpastd::error_codemelempar 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.