std::filesystem
What problem does this section solve?
Prior to C++17, performing file and directory operations—such as traversing, creating, deleting, or checking for existence—relied on platform-specific APIs (e.g., POSIX's <dirent.h>, Windows' FindFirstFile), preventing code from being cross-platform.
std::filesystem provides cross-platform file system operations, where a single codebase works across Linux, macOS, and Windows.
What is this feature?
std::filesystem is a file system library introduced in C++17 that provides features like path handling, file/directory operations, traversal, and permission queries. The core class is std::filesystem::path.
C++ standard version
C++17
Required header files
#include <filesystem>
Basic Syntax
namespace fs = std::filesystem;
fs::path p = "/home/user/data.txt"; // 路径对象
// 检查文件
fs::exists(p); // 是否存在
fs::is_regular_file(p); // 是否是普通文件
fs::is_directory(p); // 是否是目录
fs::file_size(p); // 文件大小
// 操作
fs::create_directory(p); // 创建目录
fs::copy(src, dst); // 拷贝文件
fs::remove(p); // 删除文件/目录
fs::rename(old, new); // 重命名
// 遍历
for (const auto& entry : fs::directory_iterator(p)) { ... } // 非递归
for (const auto& entry : fs::recursive_directory_iterator(p)) {} // 递归
Common operations
| Operation | Explanation |
|---|---|
fs::exists(p) | 路径是否存在 |
fs::is_directory(p) | Is this the table of contents? |
fs::is_regular_file(p) | Is this a regular file? |
fs::create_directory(p) | Create Directory |
fs::create_directories(p) | Recursively create directories (similar to mkdir -p) |
fs::copy(src, dst) | Copy files |
fs::remove(p) | Delete file or empty directory |
fs::remove_all(p) | Recursive delete (similar to rm -rf) |
fs::rename(old, new) | Rename/Move |
p.filename() | Get file name |
p.extension() | Get the extension name. |
p.parent_path() | Get parent directory |
p / "subdir" | Path concatenation (using the / operator) |
Example code
Example 1: Check file/directory existence and size information
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
// 用当前目录做演示
fs::path current = fs::current_path();
std::cout << "Current path: " << current << "\n";
std::cout << "Exists: " << std::boolalpha << fs::exists(current) << "\n";
std::cout << "Is directory: " << fs::is_directory(current) << "\n";
// 获取路径的各部分
std::cout << "Filename: " << current.filename() << "\n";
std::cout << "Parent path: " << current.parent_path() << "\n";
std::cout << "Root path: " << current.root_path() << "\n";
return 0;
}
Running results (paths may vary by machine):
Current path: "/home/user/MySource/my-blog"
Exists: true
Is directory: true
Filename: "my-blog"
Parent path: "/home/user/MySource"
Root path: "/"
Example 2: Building on Example 1, iterate through all files in the directory
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
fs::path dir = fs::current_path();
std::cout << "Contents of " << dir << ":\n";
std::cout << "--------------------------------\n";
// 遍历当前目录(不递归子目录)
for (const auto& entry : fs::directory_iterator(dir))
{
std::string type = entry.is_directory() ? "[DIR] " : "[FILE]";
std::cout << type << " " << entry.path().filename().string() << "\n";
}
return 0;
}
Running Results (vary depending on the directory contents):
Contents of "/home/user/MySource/my-blog":
--------------------------------
[DIR] .git
[DIR] app
[DIR] content
[FILE] nuxt.config.ts
[FILE] package.json
[FILE] README.md
...
Example 3: Filtering files with specific extensions based on Example 2
#include <iostream>
#include <filesystem>
#include <vector>
namespace fs = std::filesystem;
// 查找指定目录下所有 .md 文件
std::vector<fs::path> find_md_files(const fs::path& dir)
{
std::vector<fs::path> result;
for (const auto& entry : fs::recursive_directory_iterator(dir))
{
if (entry.is_regular_file() && entry.path().extension() == ".md")
{
result.push_back(entry.path());
}
}
return result;
}
int main()
{
fs::path dir = fs::current_path() / "content";
if (!fs::exists(dir))
{
std::cout << "Directory not found: " << dir << "\n";
return 1;
}
auto md_files = find_md_files(dir);
std::cout << "Found " << md_files.size() << " .md files:\n";
for (const auto& f : md_files)
{
std::cout << " " << f.filename().string() << "\n";
}
return 0;
}
Run results (depending on file content):
Found 15 .md files:
index.md
ch1-前言.md
ch18-现代C++.md
...
Example 4: Building on Example 3, create directories, files, and copy them.
#include <iostream>
#include <filesystem>
#include <fstream>
namespace fs = std::filesystem;
int main()
{
fs::path test_dir = fs::temp_directory_path() / "cpp_test_demo";
// 创建目录(如果已存在也不报错)
if (fs::create_directory(test_dir))
{
std::cout << "Created: " << test_dir << "\n";
}
else
{
std::cout << "Already exists: " << test_dir << "\n";
}
// 创建一个文件
fs::path file_path = test_dir / "hello.txt";
{
std::ofstream out(file_path);
out << "Hello, filesystem!\n";
}
std::cout << "Created file: " << file_path << "\n";
std::cout << "File size: " << fs::file_size(file_path) << " bytes\n";
// 拷贝文件
fs::path copy_path = test_dir / "hello_copy.txt";
fs::copy(file_path, copy_path);
std::cout << "Copied to: " << copy_path << "\n";
// 检查两个文件都存在
std::cout << "hello.txt exists: " << fs::exists(file_path) << "\n";
std::cout << "hello_copy.txt exists: " << fs::exists(copy_path) << "\n";
// 清理:删除测试目录
fs::remove_all(test_dir);
std::cout << "Cleaned up: " << test_dir << "\n";
return 0;
}
Results:
Created: "/tmp/cpp_test_demo"
Created file: "/tmp/cpp_test_demo/hello.txt"
File size: 21 bytes
Copied to: "/tmp/cpp_test_demo/hello_copy.txt"
hello.txt exists: true
hello_copy.txt exists: true
Cleaned up: "/tmp/cpp_test_demo"
runtime results
See the "running results" for each example above.
Key syntax explanation in the example
|Here is the translation of the provided Simplified Chinese Markdown fragment into natural American English, following all specified rules.
| Example | Discusses what | Newly emerged syntax | Why write it this way | Precautions |
|---|---|---|---|---|
| Example 1 | Path information lookup | fs::current_path()、exists()、filename() | Get the current path and its components. | All path operations are cross-platform. |
| Example 2 | Browse directory | fs::directory_iterator | Iterate over all entries in the current directory | Only traverses the first level and does not recursively enter subdirectories. |
| Example 3 | recursive traversal and filtering | fs::recursive_directory_iterator、extension() | Recursively find files with a specific suffix |
When you need to recursively locate all files that end with a particular suffix in a directory and its subdirectories, here are the most common methods by platform:
Command Line (Bash/Linux/macOS):
Use the find command.
Example: Find all .txt files starting from the current directory.
find . -type f -name "*.txt"
Command Line (PowerShell/Windows):
Use Get-ChildItem (alias gci or dir) with the -Recurse flag.
Example: Find all .log files recursively.
Get-ChildItem -Path . -Recurse -Filter *.log
Programming (Python):
Use os.walk() or the pathlib module (recommended for clarity).
Example using pathlib:
from pathlib import Path
suffix = ".py"
for p in Path('.').rglob(f'*{suffix}'):
print(p)
Programming (Java):
Use Files.walk().
Example:
import java.nio.file.*;
import java.util.stream.Stream;
String suffix = ".java";
try (Stream<Path> paths = Files.walk(Paths.get("."))) {
paths.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(suffix))
.forEach(System.out::println);
} catch (Exception e) { e.printStackTrace(); }
All these methods will traverse directories recursively to match your target file suffix.|Recursive traversal can be slow, so watch out for directory depth.|
|Example 4|create, copy, delete|create_directory()、copy()、remove_all()|Common file operations, cross-platform|remove_all is similar to rm -rf, use with caution|
Exception-based version vs error_code version
Many filesystem functions have two usage methods:
auto size = fs::file_size(path); // 失败时抛异常
std::error_code ec;
auto size = fs::file_size(path, ec); // 失败时不抛异常,错误写入 ec
| writing method | Advantages | Suitable scenarios |
|---|---|---|
| Throw Exception Version | Short code automatically interrupts on failure | The file should exist; failure is an exception. |
error_code version | Do not throw exceptions, convenient for continuous processing. | Scanning a large number of files, with uncertain permissions, allowing skipping of failed items |
Example 5: Skipping Failed Items with error_code During Directory Scanning
#include <filesystem>
#include <iostream>
#include <system_error>
// filesystem 用来跨平台处理路径、文件和目录。
namespace fs = std::filesystem;
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
fs::path dir = fs::current_path();
for (const auto& entry : fs::directory_iterator(dir))
{
std::error_code ec;
auto size = fs::file_size(entry.path(), ec);
if (ec)
{
std::cout << "[skip] " << entry.path().filename().string()
<< " reason: " << ec.message() << "\n";
continue;
}
std::cout << entry.path().filename().string()
<< " size = " << size << "\n";
}
return 0;
}
Running Results (vary depending on the directory contents):
[skip] content reason: Is a directory
package.json size = 1234
README.md size = 2048
When scanning directories, encountering directories with insufficient permissions or abnormal symbolic links is quite common. Using error_code allows the program to skip problematic items and continue running, rather than exiting upon the first failure.
Common Errors
Error 1: Forgetting to check exists before operating on it
std::cout << fs::file_size("/nonexistent"); // ❌ 抛异常!
The correct approach: perform a exists() check first, or use try-catch.
Error 2: Confusion between create_directory and create_directories
fs::create_directory("a/b/c"); // ❌ 如果 a/b 不存在则失败!
Correct approach: fs::create_directories("a/b/c"); will create recursively.
Error 3: Comparing path with ==
if (path1 == path2) // 只是字符串比较!
The paths /home/user and /home/user/ might not be equal, use fs::equivalent(p1, p2) for normalized comparison.
Error 4: -lstdc++fs is required when linking (legacy compiler)
In GCC 8 and earlier, it was necessary to link the stdc++fs library: g++ -std=c++17 file.cpp -lstdc++fs. GCC 9+ and Clang do not require this.
使用建议
- 明确目标:在开始前确定您的具体需求,以便选择最合适的工具或教程。
- 充分利用资源:参考官方文档、教程和博客,这些资料能帮助您快速上手并解决问题。
- 实践应用:通过动手操作项目或编写代码来巩固学习成果,提升实际操作能力。
- 问题解决:遇到困难时,查阅参考资料或寻求社区支持,逐步培养独立解决问题的能力。
- 分享经验:完成项目后,可以撰写文章或博客分享心得,帮助其他学习者。
如果需要针对特定领域(如单片机、机器人或环境搭建)的进一步建议,请提供更多信息,我将为您细化内容。
- Use
namespace fs = std::filesystem;to simplify code: This abbreviation is almost a community standard. - Concatenating paths with the
/operator:auto p = base / "subdir" / "file.txt";, concise and cross-platform. - Traversing large directories with
directory_iterator:Avoid loading all files at once. - Exception safety in file operations: Many filesystem functions throw
fs::filesystem_errorwhen they fail. - C++17 filesystem is generally sufficient: C++20/23 added some convenience functions but the core remains unchanged.
- Consider error_code overloads when scanning large files: Prevent a single error condition from interrupting the entire scan.
Summary
std::filesystemprovides cross-platform file system operations.fs::pathis a path class that supports joining and splitting (/,filename()/extension()/parent_path()).exists()check for existence,create_directory()create directories,copy()/remove()and operate files.- Traverse the directory,
recursive_directory_iteratorrecursively traverse. - Check
exists()before operation, paying attention to exception handling.