Working with paths and files using std::filesystem
Inspect paths, check files, create directories, and traverse folders using the C++17 filesystem library.
Learning objectives
- Build portable paths with
std::filesystem::pathand the path-join operator. - Inspect files and directories and create nested directories.
- Handle filesystem failures with exceptions or
std::error_code. - Explain why external paths, symlinks, permissions, and working directories require care.
Prerequisites: understand strings, functions, and file paths as used by file streams. Review Strings, Functions, and File I/O first. Examples use C++17.
Paths are not just strings
File paths differ across operating systems. std::filesystem::path models a path as a path object and provides operations such as joining components, inspecting filenames, and finding extensions. The library is part of C++17.
Inspect a path
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
int main() {
fs::path file = fs::current_path() / "data" / "notes.txt";
std::cout << file.filename() << '\n';
std::cout << file.extension() << '\n';
std::cout << fs::exists(file) << '\n';
}The / operator joins path components using the platform's path rules. Avoid manually concatenating separators into strings.
Create directories and inspect files
#include <filesystem>
#include <iostream>
#include <system_error>
namespace fs = std::filesystem;
int main() {
std::error_code error;
fs::create_directories("output/reports", error);
if (error) {
std::cerr << "Could not create directory: "
<< error.message() << '\n';
return 1;
}
}Many filesystem operations have overloads that report errors through std::error_code instead of throwing. Other overloads may throw std::filesystem::filesystem_error. Choose deliberately and handle failures.
Traverse a directory
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
int main() {
for (const auto& entry : fs::directory_iterator("data")) {
if (entry.is_regular_file()) {
std::cout << entry.path() << '\n';
}
}
}Directory contents can change while a program is running, and permissions or broken links can cause errors. Do not assume every entry is readable or every operation succeeds.
Security and correctness
- Validate user-supplied paths before accessing sensitive locations.
- Do not assume a path is safe merely because it begins with an expected directory string; canonicalization and symlinks complicate checks.
- Check errors and permissions.
- Use relative paths intentionally; the current working directory may differ between IDEs and terminals.
Practice
- What does the path division operator do?
- How can you create nested directories?
- Why might current_path differ between an IDE and terminal?
Show answers
- Joins path components according to platform rules.
- Call create_directories.
- Because the process is launched with a different working directory.
Summary
Use filesystem::path for portable path manipulation, check errors, and treat external paths as untrusted input.