Chapter Filesystem Library memakai path sepintas sebagai string yang disambung dengan /. Padahal std::filesystem::path adalah tipe yang jauh lebih cerdas: ia memahami pemisah platform, bisa dibedah menjadi bagian-bagiannya, dan mengetahui bagian mana nama file dan bagian mana ekstensi. Kemampuan ini penting karena program yang rapi jarang menulis path mentah — ia menyambung potongan path, mengganti nama file di ujungnya, atau menarik ekstensi untuk dipilah. Chapter ini membedah tipe path dari operator penyambung hingga komponen-komponennya, lalu melanjutkan penelusuran direktori dengan recursive_directory_iterator yang menyusuri subfolder sampai ke dalam.
Menyambung Path dengan operator/ #
Operator / pada path bekerja seperti operator penjumlahan string yang sadar platform: ia menyisipkan pemisah yang benar (/ di Linux, \ di Windows) hanya bila diperlukan. Ada juga bentuk +=-nya, yaitu path::operator/= untuk menyambung langsung ke variabel. Dengan dua baris penyambungan, kita membangun path bertingkat tanpa pernah menulis karakter pemisah sendiri — kode yang sama benar di Linux dan Windows. Untuk mewujudkan path bertingkat menjadi folder, library menyediakan create_directories (jamak) yang berperilaku seperti mkdir -p: membuat semua folder perantara yang belum ada, sekaligus folder tujuannya.
Membedah Path Menjadi Komponen #
Program berikut menyusun sebuah path, membedahnya, mewujudkannya menjadi folder dan file sungguhan, lalu menyusuri seluruh pohon yang terbentuk:
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
namespace fs = std::filesystem;
int main() {
// 1. Menyusun path dari bagian-bagian
fs::path dasar = "belajar";
fs::path folder = dasar / "modul-01";
fs::path file = folder / "catatan.txt";
std::cout << "Path lengkap: " << file.string() << '\n';
// 2. Membedah path menjadi komponen
std::cout << "parent_path : " << file.parent_path().string() << '\n';
std::cout << "filename : " << file.filename().string() << '\n';
std::cout << "stem : " << file.stem().string() << '\n';
std::cout << "extension : " << file.extension().string() << '\n';
std::cout << std::boolalpha;
std::cout << "is_absolute : " << file.is_absolute() << '\n';
// 3. Wujudkan path menjadi folder dan file nyata
fs::create_directories(folder);
{
std::ofstream catatan(file);
catatan << "isi catatan\n";
}
{
std::ofstream ringkas(dasar / "modul-01" / "ringkas.md");
ringkas << "ringkasan singkat\n";
}
// 4. Susuri seluruh pohon folder secara rekursif
std::vector<fs::path> semua;
for (const fs::directory_entry& entri :
fs::recursive_directory_iterator(dasar)) {
semua.push_back(entri.path());
}
std::sort(semua.begin(), semua.end());
std::cout << "Isi 'belajar' rekursif (" << semua.size() << " entri):\n";
for (const fs::path& p : semua) {
std::cout << " " << p.string() << '\n';
}
// 5. Bersih-bersih seluruh pohon
fs::remove_all(dasar);
std::cout << "Setelah remove_all, 'belajar' ada: "
<< fs::exists(dasar) << '\n';
return 0;
}Output yang Dihasilkan:
Path lengkap: belajar/modul-01/catatan.txt
parent_path : belajar/modul-01
filename : catatan.txt
stem : catatan
extension : .txt
is_absolute : false
Isi 'belajar' rekursif (3 entri):
belajar/modul-01
belajar/modul-01/catatan.txt
belajar/modul-01/ringkas.md
Setelah remove_all, 'belajar' ada: false- Baris 12-14 (penyambungan): tiga potongan disambung dengan
/menghasilkanbelajar/modul-01/catatan.txt. Semua pembedahan di baris berikutnya bekerja murni pada string path — folder-nya belum harus ada. - Baris 18-21 (komponen):
parent_path()mengembalikan semua sebelum pemisah terakhir,filename()bagian setelahnya,stem()filename tanpa ekstensi, danextension()mulai titik terakhir. Perhatikan ekstensi menyertakan titiknya, sehingga pencocokan menjadip.extension() == ".txt". - Baris 23 (
is_absolute()):falsekarena path tidak diawali pemisah. Pemeriksaan ini deterministik — tidak sepertifs::absolute()yang hasilnya bergantung mesin (dibahas di bawah). - Baris 26-34 (mewujudkan):
create_directoriesmembuatbelajar/modul-01lengkap dengan foldernya;ofstreammenerimapathlangsung, jadi tidak perlustring()dulu. - Baris 37-42 (iterasi rekursif):
recursive_directory_iteratormengunjungi folder, lalu isi folder itu, terus turun sampai habis — termasuk folderbelajar/modul-01itu sendiri sebagai entri. Hasilnya ditampung dan diurutkan agar output stabil, sama seperti aturan pada chapter sebelumnya. - Baris 44-46 (cetak): tiga entri tercetak — satu folder dan dua file. Untuk memilah,
directory_entrypunyais_directory()yang bisa kita pakai sebagai penyaring di dalam loop. - Baris 49-51 (bersih-bersih):
remove_allmenghapus seluruh pohon sekaligus dan mengembalikan jumlah entri yang dihapus.
Aturan pemenggalan stem/extension punya dua kasus tepi yang sering mengejutkan. Titik terakhir adalah penentu: arsip.tar.gz memiliki stem arsip.tar dan extension .gz — bukan “semua setelah titik pertama”. Dan nama yang diawali titik tanpa titik lain (gaya dotfile seperti .gitignore) diperlakukan sebagai stem utuh tanpa extension.
absolute, relative, dan current_path #
Path tanpa pemisah di depan disebut relative path: lokasinya dihitung dari current working directory saat program berjalan. Lawannya, absolute path, dimulai dari akar sistem berkas (/ di Linux, misalnya C:\ di Windows) dan selalu menunjuk lokasi yang sama ke mana pun program dijalankan. Dua fungsi yang menghubungkan keduanya: fs::current_path() mengembalikan direktori kerja saat ini sebagai path, dan fs::absolute(p) menyatukan current_path() dengan p menjadi path absolut.
Kita sengaja tidak mencetak hasil fs::absolute() di contoh terverifikasi: nilainya memuat lokasi program dijalankan, yang berbeda antara mesin dan antar direktori — persis jenis output yang harus dihindari. Pola penggunaannya tetap bermanfaat, misalnya untuk menampilkan ke pengguna di mana hasil disimpan:
fs::path hasil = "arsip/laporan.txt";
std::cout << "Hasil disimpan di: "
<< fs::absolute(hasil).string() << '\n';
// di mesin dengan cwd /home/dewi/app tercetak:
// Hasil disimpan di: /home/dewi/app/arsip/laporan.txt
string() dan Keluar dari Dunia path #
path bukan std::string, dan compiler tidak mengonversinya secara diam-diam. Saat fungsi menuntut std::string — seperti parameter kita sendiri atau API pihak ketiga — panggil p.string(). Kebalikannya juga sah: konstruktor path menerima std::string dan string literal, itulah yang kita lakukan setiap kali menulis fs::path p = "belajar";. Ada juga p.native() yang mengembalikan representasi sesuai platform (wchar_t-based di Windows); di Linux hasilnya identik dengan string(), sehingga untuk latihan lintas-platform sederhana, string() sudah memadai.
Satu catatan kecil tentang bentuk: path menyimpan string apa adanya tanpa menormalkan — fs::path("a//b/./c").string() tetap tercetak persis begitu, pemisah ganda dan semua. Normalisasi ke bentuk kanonik (menyelesaikan .., symlinks, dan bentuk absolut) ditangani fungsi terpisah, fs::canonical, yang mengharuskan target benar-benar ada.
Dengan path yang kini dikuasai penuh — menyambung, membedah, mengubah ke string, dan menyusuri pohon folder secara rekursif — peralatan sistem berkas kita praktis lengkap. Yang belum kita siapkan adalah rencana cadangan: apa yang terjadi ketika path yang diberikan tidak ada, izinnya kurang, atau file yang mau dibaca ternyata tidak terbuka? Chapter berikutnya membahas persis itu — penanganan galat untuk seluruh operasi file.