File management in C++ involves renaming, moving, or deleting filesystem entries from our program.
In the previous article, we learned how to create and read files. But managing files involves more than just looking at their contents. Sometimes we need to perform maintenance tasks: changing names, moving files between folders, or deleting them.
In the old days (before C++17), this was a headache. We had to resort to inherited C functions (<cstdio>) that were clunky and varied between Windows and Linux.
Fortunately, C++17 introduced the <filesystem> library, which standardizes all these operations and makes them much safer and more powerful.
To use the examples in this article, you need a compiler compatible with C++17 or higher.
The std::filesystem Library
To get started, we need to include the library. For convenience, it’s very common to create an alias for the namespace, since std::filesystem is quite long to write.
#include <iostream>
#include <filesystem> // Required
namespace fs = std::filesystem; // Alias for less typing
Now we can access all functions using fs::.
Renaming or Moving Files
The fs::rename function is used to change a file’s name and, when the system allows it, move it from one directory to another.
void renameFile() {
fs::path source = "old.txt";
fs::path destination = "new.txt";
try {
fs::rename(source, destination);
std::cout << "File renamed successfully.\n";
}
catch (fs::filesystem_error& e) {
std::cerr << "Error: " << e.what() << "\n";
}
}
Caution: If the destination file already exists, the behavior may depend on the system and the type of path. If you don’t want to overwrite or run into weird errors, check fs::exists(destination) beforehand.
Moving to Another Folder
If you specify a different folder in the destination path, the file will be moved.
// Moves the file to the 'backup' subfolder
fs::rename("data.txt", "backup/data_2023.txt");
The backup folder must already exist, otherwise it will throw an error.
Deleting Files
We have two main functions for deleting files:
fs::remove(path): Deletes a single file or an empty directory. Returnstrueif something was deleted,falseif it didn’t exist.fs::remove_all(path): Deletes a file or a directory and all its contents recursively.
void deleteFile() {
fs::path file = "trash.tmp";
// Safe way: check first
if (fs::exists(file)) {
fs::remove(file);
std::cout << "File deleted.\n";
} else {
std::cout << "The file does not exist.\n";
}
}
Delete operations in C++ are permanent. Files do not go to the recycle bin. They are permanently erased from the disk.
Error Handling
Working with the filesystem is error-prone (the file is missing, you lack permissions, the disk is full).
std::filesystem has two ways to report errors:
- Exceptions (Default): As we saw in the first example, if something fails, it throws
fs::filesystem_error. - Error Code (No-throw): If you prefer not to use
try-catch, you can pass an error variable as the last argument.
std::error_code ec;
fs::rename("a.txt", "b.txt", ec); // Does not throw exception
if (ec) {
std::cout << "Failure: " << ec.message() << "\n";
}
The Old C Method
You might encounter old code that doesn’t use <filesystem>. In that case, you’ll see functions from the C library <cstdio>.
std::rename(const char* old, const char* new)std::remove(const char* path)
These functions work with C-style strings (char*), so if you use std::string you’ll need to use .c_str().
#include <cstdio>
// Old style (NOT RECOMMENDED in modern C++)
if (std::remove("file.txt") != 0) {
perror("Error deleting file");
}
The problem with these functions is that error handling is much poorer, and they don’t handle paths with special characters or unicode well across different operating systems (Windows vs Linux).