C++ - C++ Filesystem Library (std::filesystem)

The C++ Filesystem Library, introduced officially in C++17, provides a standard and portable way to work with files, directories, paths, and other filesystem-related operations. Before C++17, programmers often had to depend on operating-system-specific APIs or external libraries to perform operations such as creating directories, checking whether a file exists, copying files, or obtaining file sizes. The std::filesystem library, available through the <filesystem> header, provides a consistent interface for these tasks.

1. Including the Filesystem Library

To use filesystem functionality, include the <filesystem> header.

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main() {
    fs::path filePath = "example.txt";

    std::cout << filePath << std::endl;

    return 0;
}

The namespace std::filesystem contains all the important classes and functions. Using an alias such as fs makes the code shorter and easier to read.

2. Understanding std::filesystem::path

The path class represents a location in a filesystem. It can represent a file path, directory path, absolute path, or relative path.

fs::path path1 = "documents/report.txt";
fs::path path2 = "/home/user/documents";

A path can also be constructed using multiple components.

fs::path filePath = fs::path("documents") / "reports" / "report.txt";

The / operator is overloaded for paths, making it convenient to combine path components.

For example:

documents/reports/report.txt

The actual separator can vary according to the operating system.

3. Checking Whether a File or Directory Exists

One of the most common filesystem operations is checking whether a particular path exists.

fs::path path = "example.txt";

if (fs::exists(path)) {
    std::cout << "Path exists." << std::endl;
} else {
    std::cout << "Path does not exist." << std::endl;
}

The exists() function returns true if the specified path exists and false otherwise.

This is useful before attempting operations such as opening, copying, deleting, or modifying a file.

4. Determining Whether a Path Is a File or Directory

The filesystem library provides separate functions for identifying different types of paths.

fs::path path = "example.txt";

if (fs::is_regular_file(path)) {
    std::cout << "It is a regular file." << std::endl;
}

Similarly, directories can be checked using:

if (fs::is_directory(path)) {
    std::cout << "It is a directory." << std::endl;
}

Other functions include:

fs::is_symlink(path);
fs::is_block_file(path);
fs::is_character_file(path);

These functions allow applications to determine what kind of filesystem object they are dealing with.

5. Creating Directories

The create_directory() function can be used to create a new directory.

fs::create_directory("Reports");

For creating nested directories, create_directories() is more useful.

fs::create_directories("Company/Projects/2026/Reports");

If the required parent directories do not exist, create_directories() creates them automatically.

For example, if Company and Projects do not exist, the function can create the complete hierarchy.

6. Removing Files and Directories

The remove() function can remove a file or an empty directory.

fs::remove("example.txt");

For removing a directory and its contents recursively, remove_all() can be used.

fs::remove_all("TemporaryFiles");

remove_all() should be used carefully because it can remove an entire directory tree and its contents.

7. Copying Files and Directories

The filesystem library provides copy() for copying filesystem objects.

fs::copy("source.txt", "destination.txt");

Copying a directory can require appropriate copy options.

fs::copy(
    "SourceFolder",
    "BackupFolder",
    fs::copy_options::recursive
);

The recursive option tells C++ to copy the contents of subdirectories as well.

Other useful options include:

fs::copy_options::overwrite_existing
fs::copy_options::skip_existing
fs::copy_options::recursive

These options provide greater control over the copying operation.

8. Renaming and Moving Files

The rename() function can be used both for renaming and moving filesystem objects.

fs::rename("old.txt", "new.txt");

It can also move a file to another directory.

fs::rename("old.txt", "Documents/old.txt");

Whether this behaves as expected can depend on the filesystem and operating system, particularly when moving objects across different filesystems.

9. Obtaining File Size

The file_size() function returns the size of a regular file in bytes.

fs::path file = "example.txt";

if (fs::exists(file) && fs::is_regular_file(file)) {
    std::cout << "File size: "
              << fs::file_size(file)
              << " bytes" << std::endl;
}

This can be useful when applications need to monitor storage usage or display information about files.

10. Iterating Through a Directory

The directory_iterator class makes it possible to examine the contents of a directory.

for (const auto& entry : fs::directory_iterator("Documents")) {
    std::cout << entry.path() << std::endl;
}

This displays the files and directories immediately inside the Documents directory.

For example, if the directory contains:

Documents/
    report.txt
    photo.jpg
    Projects/

The iterator can access each of these entries.

11. Recursively Traversing Directories

For applications that need to examine all files and subdirectories, recursive_directory_iterator can be used.

for (const auto& entry :
     fs::recursive_directory_iterator("Documents")) {

    std::cout << entry.path() << std::endl;
}

Unlike directory_iterator, this also enters subdirectories and examines their contents.

This is useful for applications such as:

  • File indexing systems

  • Backup applications

  • Directory analysis tools

  • Search utilities

  • Storage management programs

12. Working With File Extensions

The path class provides several useful functions for examining path components.

fs::path file = "documents/report.pdf";

std::cout << file.filename() << std::endl;
std::cout << file.extension() << std::endl;
std::cout << file.stem() << std::endl;

The output would conceptually be:

report.pdf
.pdf
report

Other useful functions include:

file.parent_path();
file.root_path();
file.relative_path();
file.filename();
file.extension();
file.stem();

These make it easier to analyze and manipulate file paths.

13. Absolute and Relative Paths

A relative path describes a location relative to the current working directory.

fs::path path = "documents/report.txt";

An absolute path specifies the complete location.

fs::path path = fs::absolute("documents/report.txt");

The absolute() function can convert a relative path into an absolute path.

C++ also provides:

fs::current_path();

to obtain the application's current working directory.

Example:

std::cout << fs::current_path() << std::endl;

The current working directory is important because relative paths are interpreted based on it.

14. Changing the Current Working Directory

The current working directory can be changed using current_path().

fs::current_path("Documents");

After this operation, relative paths are interpreted relative to the new working directory.

Applications should use this carefully because changing the current working directory can affect other filesystem operations performed by the program.

15. Checking File Permissions

The filesystem library also provides facilities for examining and modifying permissions.

auto permissions = fs::status("example.txt").permissions();

Permissions can be modified using permissions().

For example:

fs::permissions(
    "example.txt",
    fs::perms::owner_read |
    fs::perms::owner_write
);

Permission behavior can vary between operating systems, so developers should account for platform-specific differences.

16. Error Handling

Filesystem operations can fail for many reasons. A file may not exist, permission may be denied, a directory may be inaccessible, or a path may be invalid.

The filesystem library can report errors using std::error_code.

std::error_code error;

bool result = fs::create_directory("Reports", error);

if (error) {
    std::cout << "Error: "
              << error.message()
              << std::endl;
}

This approach allows a program to handle errors without necessarily throwing an exception.

Many filesystem functions also have versions that throw std::filesystem::filesystem_error when an operation fails.

Example:

try {
    fs::remove("example.txt");
}
catch (const fs::filesystem_error& error) {
    std::cout << "Filesystem error: "
              << error.what()
              << std::endl;
}

17. Checking Available Storage

C++ also provides space() for obtaining information about storage capacity.

fs::space_info info = fs::space(".");

std::cout << "Capacity: " << info.capacity << std::endl;
std::cout << "Free: " << info.free << std::endl;
std::cout << "Available: " << info.available << std::endl;

The values are generally expressed in bytes.

This can be useful for applications that need to check whether sufficient storage is available before creating or copying large files.

18. Complete Example

The following program demonstrates several filesystem operations together.

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main() {

    fs::path folder = "MyDocuments";

    if (!fs::exists(folder)) {
        fs::create_directory(folder);
        std::cout << "Directory created." << std::endl;
    }

    for (const auto& entry : fs::directory_iterator(".")) {

        if (fs::is_regular_file(entry.path())) {
            std::cout << "File: "
                      << entry.path()
                      << std::endl;
        }
    }

    return 0;
}

The program first creates a directory if it does not already exist. It then examines the current directory and displays paths that represent regular files.

19. Advantages of std::filesystem

The C++ filesystem library provides several important advantages.

Portability: It provides a standard interface that works across major operating systems.

Convenience: Operations such as copying, deleting, renaming, and creating directories can be performed without writing operating-system-specific code.

Path management: The path class makes path construction and analysis much easier.

Directory traversal: Iterators provide a convenient way to process directory contents.

Error handling: Both exceptions and std::error_code can be used to handle filesystem failures.

Modern C++ integration: It works naturally with other features of the C++ standard library.

20. Important Functions to Remember

Function/Class Purpose
fs::path Represents a filesystem path
fs::exists() Checks whether a path exists
fs::is_regular_file() Checks whether a path is a regular file
fs::is_directory() Checks whether a path is a directory
fs::create_directory() Creates one directory
fs::create_directories() Creates a directory hierarchy
fs::remove() Removes a file or empty directory
fs::remove_all() Recursively removes a directory and its contents
fs::copy() Copies files or directories
fs::rename() Renames or moves filesystem objects
fs::file_size() Gets the size of a file
fs::directory_iterator Iterates through directory contents
fs::recursive_directory_iterator Recursively traverses directories
fs::current_path() Gets or changes the current working directory
fs::absolute() Converts a path to an absolute path
fs::space() Obtains storage information

Conclusion

The C++ std::filesystem library provides a standardized way to interact with the operating system's filesystem. It allows programmers to create and remove directories, manipulate files, inspect paths, traverse directory structures, determine file properties, manage permissions, and monitor storage space.

It is particularly important for applications such as file managers, backup utilities, document management systems, search tools, installers, logging systems, and storage-management applications. Since it became part of the standard library in C++17, programmers can perform many filesystem operations without relying on platform-specific libraries, making their applications more portable and maintainable.