C++ Filesystem
- Description: A note on
std::filesystem(C++17) —path, queries, traversal, file operations, error handling, and cross-platform considerations - My Notion Note ID: K2A-B1-23
- Created: 2018-07-10
- Updated: 2026-02-28
- License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io
Table of Contents
- 1.
std::filesystem::path - 2. Queries: Existence, Type, Size, Time
- 3. Iterating Directories
- 4. File Operations
- 5. Error Handling
- 6. Cross-Platform Considerations
1. std::filesystem::path
std::filesystem::path (C++17, <filesystem>) is the type for filesystem locations. It handles encoding, separators, and decomposition uniformly across platforms.
#include <filesystem>
namespace fs = std::filesystem;
fs::path p = "/var/log/app.log";
p.string(); // "/var/log/app.log"
p.filename(); // "app.log"
p.stem(); // "app"
p.extension(); // ".log"
p.parent_path(); // "/var/log"
p.replace_extension(".bak"); // "/var/log/app.bak"
// Composition with /= and /
fs::path dir = "/var/log";
fs::path file = dir / "app.log"; // "/var/log/app.log"
file /= "rotated"; // "/var/log/app.log/rotated"
// /= treats both sides as directory parts
Path arithmetic (/, /=) handles the platform separator automatically. Don't manually concatenate strings.
2. Queries: Existence, Type, Size, Time
namespace fs = std::filesystem;
fs::path p = "/etc/hosts";
fs::exists(p); // true
fs::is_regular_file(p); // true
fs::is_directory(p); // false
fs::is_symlink(p); // false
fs::is_empty(p); // false (file is non-empty)
fs::file_size(p); // size in bytes (regular files only)
auto t = fs::last_write_time(p); // file_clock time_point
auto s = fs::status(p); // permissions + type (cached)
auto perms = s.permissions(); // permission bits
status(p) is the cheapest single-stat query; the boolean helpers like is_directory(p) re-stat the path each call. For tight loops, cache:
auto s = fs::status(p);
if (fs::is_regular_file(s)) { /* ... */ } // reuses the cached status
3. Iterating Directories
namespace fs = std::filesystem;
// Non-recursive
for (const auto& entry : fs::directory_iterator{"/var/log"}) {
std::cout << entry.path() << "\n";
}
// Recursive
for (const auto& entry : fs::recursive_directory_iterator{"/var/log"}) {
if (entry.is_regular_file()) {
std::cout << entry.path() << " (" << entry.file_size() << ")\n";
}
}
Directory iterators return directory_entry objects, which cache file status for cheap repeated queries.
4. File Operations
namespace fs = std::filesystem;
// Create
fs::create_directory("/tmp/foo");
fs::create_directories("/tmp/foo/bar/baz"); // mkdir -p
// Copy / move
fs::copy("a.txt", "b.txt");
fs::copy("src/", "dst/", fs::copy_options::recursive | fs::copy_options::overwrite_existing);
fs::rename("old.txt", "new.txt");
// Remove
fs::remove("file.txt"); // single file
fs::remove_all("/tmp/foo"); // recursive (returns count)
// Symlinks
fs::create_symlink("/var/log/app.log", "current.log");
fs::read_symlink("current.log"); // "/var/log/app.log"
// Permissions
fs::permissions("script.sh", fs::perms::owner_exec, fs::perm_options::add);
5. Error Handling
Every filesystem function has two overloads:
- Throws
std::filesystem::filesystem_erroron failure. - Takes a
std::error_code&and reports failure through it (no throw).
namespace fs = std::filesystem;
// Throwing version
try {
auto sz = fs::file_size("missing.txt");
} catch (const fs::filesystem_error& e) {
std::cerr << e.what() << " path: " << e.path1() << "\n";
}
// Non-throwing version
std::error_code ec;
auto sz = fs::file_size("missing.txt", ec);
if (ec) {
std::cerr << "error: " << ec.message() << "\n";
}
Pick whichever matches your existing error-handling style. See C++ Error Handling § 6.
6. Cross-Platform Considerations
- Encoding. On POSIX, paths are
char(typically UTF-8). On Windows, they arewchar_t(UTF-16).path::native()returns the platform-native representation;path::string()converts (lossy on Windows for non-Latin filenames, usepath::u8string()for UTF-8). - Separators. Use
/in literals;pathtranslates as needed. Avoid hardcoding backslashes on Windows. - Case sensitivity. POSIX is case-sensitive; Windows and macOS HFS+/APFS default to case-insensitive but case-preserving. Don't rely on case-only differences.
- Permissions. POSIX
permsmap cleanly; Windows ACLs do not, so thepermissions()API is approximate on Windows. - Symbolic links. Windows requires admin or developer mode to create symlinks; the call may fail otherwise.