C++ PROGRAMMING ยท 12 MIN STUDY

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::path and 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

  1. What does the path division operator do?
  2. How can you create nested directories?
  3. Why might current_path differ between an IDE and terminal?
Show answers
  1. Joins path components according to platform rules.
  2. Call create_directories.
  3. 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.

References