Wiki
std::filesystem
Existing English translations were reused. 8 current source blocks remain in Simplified Chinese, without triggering paid translation.
1.What problem does this section solve?
Before C++17, manipulating files and directories (traversing, creating, deleting, checking for existence) required relying on platform-specific APIs (such as POSIX's <dirent.h> and Windows's FindFirstFile), and the code could not be cross-platform.
std::filesystem providescross-platform file system operations; one codebase works on Linux, macOS, and Windows.
2.What is this feature?
std::filesystem 是 C++17 引入的文件系统库,提供了路径处理、文件/目录操作、遍历、权限查询等功能。核心类是 std::filesystem::path。
3.C++ Standard Edition
C++17
4.Required header files
#include <filesystem>
5.Basic syntax
namespace fs = std::filesystem;
fs::path p = "data/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)) {} // 递归
6.Linux 和 Windows 中的路径写法
上面的相对路径在两种系统中都可以使用;后面示例如果展示 /home/user/...,那是 Linux 的一次运行结果,Windows 会显示自己的盘符和路径。
Windows 绝对路径可以使用原始字符串,拼接目录时优先交给 path:
fs::path folder = R"(C:\work\data)";
fs::path file = folder / "result.txt";
相对路径以 fs::current_path() 为基准。fs::temp_directory_path() 可以取得当前系统的临时目录,不需要把 Linux 的 /tmp 写死。不同系统的权限和路径显示形式也可能不同,示例输出主要用于理解各字段的含义。
7.Common Operations
| 操作 | 说明 |
|---|---|
fs::exists(p) |
路径是否存在 |
fs::is_directory(p) |
是否目录 |
fs::is_regular_file(p) |
是否普通文件 |
fs::create_directory(p) |
创建目录 |
fs::create_directories(p) |
递归创建目录(类似 mkdir -p) |
fs::copy(src, dst) |
拷贝文件 |
fs::remove(p) |
删除文件或空目录 |
fs::remove_all(p) |
递归删除(类似 rm -rf) |
fs::rename(old, new) |
重命名/移动 |
p.filename() |
获取文件名 |
p.extension() |
获取扩展名 |
p.parent_path() |
获取父目录 |
p / "subdir" |
路径拼接(用 / 运算符) |
8.Example code
8.1.示例 1:检查文件/目录是否存在和大小的信息
#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;
}
Run results(the path varies by machine):
Current path: "/home/user/MySource/my-blog"
Exists: true
Is directory: true
Filename: "my-blog"
Parent path: "/home/user/MySource"
Root path: "/"
8.2.Example 2: Based on Example 1, traverse 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 result(varies depending on 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
...
8.3.Example 3: On the basis of Example 2, filter files with specific extensions
#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;
}
Execution result(varies depending on file content):
Found 15 .md files:
index.md
ch2-前言.md
2000-现代C++.md
...
8.4.Example 4: Based on Example 3, create directories, files, and copy
#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;
}
Running result:
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"
9.Operation result
See the "run results" for each example above.
10.Key syntax explanation in the example
| Example | What it explains | Newly introduced syntax | Why it is written this way | Notes |
|---|---|---|---|---|
| Example 1 | Path information query | fs::current_path()、exists()、filename() |
Get the current path and its components | All path operations are cross-platform |
| Example 2 | Traverse directory | fs::directory_iterator |
Traverse all entries in the current directory | Only traverses one level; does not recurse into subdirectories |
| Example 3 | Recursive traversal and filtering | fs::recursive_directory_iterator、extension() |
Recursively find files with a specific extension | Recursive traversal can be slow; pay attention to directory depth |
| Example 4 | Create, copy, delete | create_directory()、copy()、remove_all() |
Common file operations, cross-platform | remove_all Similar to rm -rf; use with caution |
11.Exception-throwing version vs error_code version
Many filesystem functions have two usage patterns:
auto size = fs::file_size(path); // 失败时抛异常
std::error_code ec;
auto size = fs::file_size(path, ec); // 失败时不抛异常,错误写入 ec
| Writing style | Advantages | Suitable scenarios |
|---|---|---|
| Exception-throwing version | Code is shorter, automatically interrupts on failure | The file should exist; failure is an exception |
error_code version |
Does not throw exceptions, making it easier to continue processing | Scanning a large number of files, uncertain permissions, allowing failed items to be skipped |
11.1.Example 5: Use error_code to skip failed items when scanning a directory
#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 result(varies depending on directory contents):
[skip] content reason: Is a directory
package.json size = 1234
README.md size = 2048
When scanning directories, encountering directories, insufficient permissions, and abnormal symbolic links is very common. Use error_code to allow the program to skip bad entries and continue running, rather than exiting on the first failure.
12.Common Errors
Error 1: Forgetting to check exists before operating directly
std::cout << fs::file_size("/nonexistent"); // ❌ 抛异常!
Correct approach: first exists() check, or use try-catch.
Error 2:create_directory and create_directories confusion
fs::create_directory("a/b/c"); // ❌ 如果 a/b 不存在则失败!
Correct approach:fs::create_directories("a/b/c"); will be created recursively.
Error 3: Using == to Compare Paths
if (path1 == path2) // 只是字符串比较!
Path /home/user and /home/user/ may not be equal; use fs::equivalent(p1, p2) for canonical comparison.
Error 4: When linking, -lstdc++fs is required (old compilers)
In GCC 8 and earlier, you need to link the stdc++fs library:g++ -std=c++17 file.cpp -lstdc++fsGCC 9+ and Clang do not require it.
13.Usage Recommendations
- Use
namespace fs = std::filesystem;to simplify code:this abbreviation is almost a community standard. - Join paths with the
/operator:auto p = base / "subdir" / "file.txt";,concise and cross-platform. - When traversing large directories, use
directory_iterator: avoid loading all files at once. - Pay attention to exception safety in file operations:many filesystem functions throw
fs::filesystem_erroron failure. - C++17's filesystem is basically sufficient:C++20/23 add some convenience functions, but the core remains unchanged.
- When scanning a large number of files, consider the error_code overload:avoid having one exception path interrupt the entire scan.
14.Summary
std::filesystem提供跨平台的文件系统操作。fs::path是路径类,支持/拼接、拆分(filename()/extension()/parent_path())。exists()检查存在,create_directory()创建目录,copy()/remove()等操作文件。directory_iterator遍历目录,recursive_directory_iterator递归遍历。- 操作前检查
exists(),注意异常处理。