在现代C++中,文件系统操作已经被标准化为 std::filesystem,它位于 <filesystem> 头文件中,并在 C++17 标准里正式加入。相比传统的 POSIX API 或 Windows API,std::filesystem 提供了更高层次、跨平台且更安全的接口。下面从概念、使用示例、常见问题以及性能考虑四个方面,详细阐述如何在 C++17 环境下使用 std::filesystem 进行文件与目录的创建、删除、遍历、属性查询等操作。
1. 关键概念
| 术语 | 说明 |
|---|---|
path |
表示文件或目录路径的对象。内部采用字符串存储,但提供了自动分隔符转换(Windows 使用 \,Unix 使用 /) |
directory_entry |
目录条目,包含路径、文件类型、大小等信息 |
file_status |
文件状态信息(类型、权限) |
filesystem_error |
异常类型,用于捕获文件系统错误(如权限不足、路径不存在等) |
使用 std::filesystem 时,最常用的对象是 std::filesystem::path。它支持构造、拼接、比较以及与标准字符串相互转换。
2. 基本使用示例
以下示例演示了常见的文件系统操作:创建目录、复制文件、遍历目录、获取文件大小、检查是否存在以及移动文件。
#include <iostream>
#include <filesystem>
#include <fstream>
namespace fs = std::filesystem;
int main() {
try {
// 1. 创建一个多层目录
fs::path dir = "example_dir/subdir";
fs::create_directories(dir);
std::cout << "Created directories: " << dir << std::endl;
// 2. 创建一个文本文件并写入内容
fs::path file = dir / "sample.txt";
std::ofstream ofs(file);
ofs << "Hello, std::filesystem!" << std::endl;
ofs.close();
// 3. 复制文件到同级目录
fs::path copy = dir / "sample_copy.txt";
fs::copy_file(file, copy, fs::copy_options::overwrite_existing);
std::cout << "Copied file to: " << copy << std::endl;
// 4. 遍历目录
std::cout << "Listing all files in " << dir << ":" << std::endl;
for (const auto& entry : fs::directory_iterator(dir)) {
std::cout << " " << entry.path().filename() << (entry.is_directory() ? " [dir]" : " [file]") << std::endl;
}
// 5. 获取文件大小
auto sz = fs::file_size(copy);
std::cout << "Size of " << copy << ": " << sz << " bytes" << std::endl;
// 6. 判断路径是否存在
if (fs::exists(copy)) {
std::cout << copy << " exists." << std::endl;
}
// 7. 移动文件
fs::path new_location = dir / "sample_final.txt";
fs::rename(copy, new_location);
std::cout << "Moved file to: " << new_location << std::endl;
// 8. 删除文件
fs::remove(new_location);
std::cout << "Deleted file: " << new_location << std::endl;
// 9. 删除目录(递归)
fs::remove_all("example_dir");
std::cout << "Deleted directory: example_dir" << std::endl;
} catch (const fs::filesystem_error& e) {
std::cerr << "Filesystem error: " << e.what() << '\n';
std::cerr << "Path: " << e.path1() << '\n';
if (!e.path2().empty())
std::cerr << "Other path: " << e.path2() << '\n';
}
return 0;
}
说明
create_directories():递归创建多层目录;若目录已存在,则不抛异常。copy_file():复制文件,overwrite_existing选项会覆盖目标文件。directory_iterator:返回所有目录条目;若想忽略符号链接,可使用recursive_directory_iterator。file_size():获取文件大小,若文件不存在会抛异常。remove():删除单个文件;若是目录会抛异常。remove_all():递归删除目录及其内容。
3. 常见陷阱与最佳实践
| 场景 | 说明 |
|---|---|
| 路径分隔符 | 直接使用字符串拼接可能导致平台差异。建议使用 path / "subdir" 语法。 |
| 异常处理 | 大部分文件系统函数会抛 filesystem_error。可在需要容错的地方捕获或使用 exists()、is_regular_file() 等函数提前判断。 |
| 权限 | 在 Windows 上使用 fs::permissions() 可以设置文件权限;在 POSIX 上则需要使用 chmod 兼容接口。 |
| 符号链接 | directory_iterator 默认会跟随符号链接。若不想跟随,需使用 directory_options::skip_permission_denied 或手动检查 is_symlink(). |
| 文件名 Unicode | Windows 的 std::filesystem::path 默认使用 UTF-16 内部编码;在 Linux 则使用 UTF-8。若跨平台编译,最好使用 path.string() 或 path.u8string()。 |
| 性能 | 对于大目录,使用 recursive_directory_iterator 并结合 path.filename() 进行过滤,可减少 I/O。 |
4. 性能考虑
- 批量操作:如果需要一次性移动或复制大量文件,最好在同一文件系统内使用
rename(),因为它只修改目录项,速度远快于copy_file()+remove()。 - IO 合并:在高并发写文件时,考虑使用
std::ofstream的std::ios::app或std::ios::binary模式,并避免频繁打开/关闭文件。 - 缓存:
std::filesystem的缓存机制不透明,若对性能极度敏感,可考虑直接使用底层系统 API。
5. 结语
std::filesystem 让 C++ 开发者摆脱了繁琐的系统特定 API,提供了统一、类型安全且易用的文件操作接口。掌握了其基本使用方法后,后续在项目中进行跨平台开发时,文件系统的代码将更简洁、错误更少。希望这篇文章能帮助你在 C++17 环境下快速上手 std::filesystem,并在实际项目中灵活运用。