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.