C++17 之 std::filesystem:文件路径操作新手指南

C++17 标准库在 头文件中引入了一个强大的文件系统交互 API,极大地方便了路径拼接、文件查询、遍历以及文件属性获取等常见任务。本文将从最基础的路径操作讲起,演示如何使用 std::filesystem 完成一系列实用的文件系统任务,并简要讨论跨平台兼容性与性能注意事项。

1. 引入头文件与命名空间

#include <filesystem>
namespace fs = std::filesystem;

std::filesystem 只在 C++17 及以上可用,编译时需使用 -std=c++17 或更高的标准。

2. 路径类型与基本操作

std::filesystem::path 是一种轻量级对象,用来表示文件系统路径。它支持各种构造与操作:

fs::path p1("/usr");                     // 绝对路径
fs::path p2("Documents/report.txt");     // 相对路径
fs::path p3 = p1 / p2;                   // 连接路径

operator/ 用于拼接子路径,内部会根据操作系统自动插入分隔符。

  • 查询路径信息
    std::cout << "Path: " << p3 << '\n';
    std::cout << "Filename: " << p3.filename() << '\n';   // report.txt
    std::cout << "Extension: " << p3.extension() << '\n'; // .txt
  • 绝对化与相对化
    fs::path abs = fs::absolute(p3);          // 把相对路径变成绝对路径
    fs::path rel = fs::relative(abs, "/usr"); // 从指定根生成相对路径

3. 文件与目录查询

3.1 目录遍历

使用 fs::directory_iterator(非递归)或 fs::recursive_directory_iterator(递归)遍历目录:

for (const auto& entry : fs::directory_iterator("/usr/bin")) {
    std::cout << entry.path() << '\n';
}

3.2 检查文件/目录状态

fs::path file = "/etc/passwd";
if (fs::exists(file) && fs::is_regular_file(file)) {
    std::cout << file << " exists and is a regular file.\n";
}
  • fs::status() 获取文件状态信息(如大小、权限)。
  • fs::last_write_time() 读取最后修改时间。

4. 文件操作

4.1 复制、移动与删除

fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
fs::rename(src, dst);               // 移动或重命名
fs::remove(file);                   // 删除文件
fs::remove_all(dir);                // 删除目录及其子文件

4.2 创建目录

fs::create_directory("/tmp/newdir");
fs::create_directories("/tmp/a/b/c"); // 自动创建多级目录

5. 示例:统计项目根目录下的所有源文件大小

fs::path root = "/home/user/project";
uintmax_t total_size = 0;

for (const auto& entry : fs::recursive_directory_iterator(root)) {
    if (entry.is_regular_file() && entry.path().extension() == ".cpp") {
        total_size += entry.file_size();
    }
}
std::cout << "Total .cpp size: " << total_size << " bytes\n";

6. 跨平台注意事项

  • 路径分隔符fs::path 会根据平台自动使用 /(Linux/Unix/macOS)或 \\(Windows)。但在字符串常量中仍建议使用 /,因为 std::filesystem 会自动转换。
  • 大写与小写敏感:Windows 文件系统不区分大小写,而 Linux 是区分的。编写代码时保持一致性,避免因大小写导致的不可预料错误。
  • 编码问题:Windows 的 std::filesystem::path 默认使用 UTF-16(wchar_t),而 Linux 使用 UTF-8(char)。若在跨平台项目中需要统一编码,可使用 fs::path::string()wstring() 进行转换。

7. 性能提示

  • 使用 is_regular_file() 而非 exists() + is_regular_file():后者会导致两次系统调用,前者一次即可完成。
  • 遍历目录时可使用 std::filesystem::directory_options::skip_permission_denied:忽略无权限目录,避免抛异常。
  • 避免频繁的磁盘 I/O:若要批量操作文件,先收集路径再一次性执行即可。

8. 结语

C++17 的 std::filesystem 为文件系统交互提供了统一、类型安全且跨平台的 API。掌握基本路径操作、目录遍历与文件管理后,你可以轻松实现复杂的文件系统工具。未来在 C++20 及更高版本中,std::filesystem 将得到进一步的性能优化与新特性扩展,值得持续关注。祝你在项目中玩得开心,文件管理事半功倍!

发表评论