filesystem — 文件系统
C++17 引入
<filesystem>,提供跨平台的文件系统操作,告别手写 POSIX/Win32 代码。
路径操作
cpp
#include <filesystem>
namespace fs = std::filesystem;
// 构造路径
fs::path p1 = "/home/user/docs/file.txt";
fs::path p2 = "relative/path";
fs::path p3 = fs::current_path() / "data" / "input.csv"; // 路径拼接
// 路径分解
fs::path p = "/home/user/docs/file.txt";
p.root_path(); // "/"
p.parent_path(); // "/home/user/docs"
p.filename(); // "file.txt"
p.stem(); // "file"(不含扩展名)
p.extension(); // ".txt"
// 路径操作
p.replace_extension(".md"); // "/home/user/docs/file.md"
p.replace_filename("other.txt");
// 转换
std::string s = p.string(); // 平台原生字符串
std::string u8s = p.u8string(); // UTF-8 字符串
p.generic_string(); // 使用 '/' 的通用格式
// 路径规范化
fs::path messy = "/home/user/../user/./docs";
fs::path clean = fs::canonical(messy); // "/home/user/docs"(需要路径存在)
fs::path lexical = messy.lexically_normal(); // 不需要路径存在文件与目录操作
cpp
// 检查
fs::exists("/tmp/file.txt");
fs::is_regular_file("/tmp/file.txt");
fs::is_directory("/tmp");
fs::is_symlink("/tmp/link");
// 创建
fs::create_directory("/tmp/newdir");
fs::create_directories("/tmp/a/b/c"); // 递归创建
// 复制
fs::copy_file("src.txt", "dst.txt");
fs::copy_file("src.txt", "dst.txt", fs::copy_options::overwrite_existing);
fs::copy("src_dir", "dst_dir", fs::copy_options::recursive);
// 移动/重命名
fs::rename("old.txt", "new.txt");
// 删除
fs::remove("file.txt");
fs::remove_all("directory"); // 递归删除
// 文件大小
uintmax_t size = fs::file_size("file.txt");
// 最后修改时间
auto ftime = fs::last_write_time("file.txt");
// C++20 转换为 time_t
auto sctp = std::chrono::file_clock::to_sys(ftime);目录遍历
cpp
// 遍历目录(非递归)
for (const auto& entry : fs::directory_iterator("/tmp")) {
std::cout << entry.path() << "\n";
if (entry.is_regular_file()) {
std::cout << " 大小: " << entry.file_size() << "\n";
}
}
// 递归遍历
for (const auto& entry : fs::recursive_directory_iterator("/home/user")) {
if (entry.is_regular_file() && entry.path().extension() == ".cpp") {
std::cout << entry.path() << "\n";
}
}
// 过滤特定类型文件
std::vector<fs::path> cpp_files;
for (const auto& entry : fs::recursive_directory_iterator(".")) {
if (entry.is_regular_file() && entry.path().extension() == ".cpp") {
cpp_files.push_back(entry.path());
}
}
// 统计目录大小
uintmax_t total_size = 0;
for (const auto& entry : fs::recursive_directory_iterator(".")) {
if (entry.is_regular_file()) {
total_size += entry.file_size();
}
}
std::cout << "总大小: " << total_size / 1024 / 1024 << " MB\n";错误处理
cpp
// 方式 1:异常(默认)
try {
fs::copy_file("nonexistent.txt", "dst.txt");
} catch (const fs::filesystem_error& e) {
std::cerr << "错误: " << e.what() << "\n";
std::cerr << "路径1: " << e.path1() << "\n";
std::cerr << "路径2: " << e.path2() << "\n";
std::cerr << "错误码: " << e.code() << "\n";
}
// 方式 2:错误码(不抛异常,性能更好)
std::error_code ec;
fs::copy_file("nonexistent.txt", "dst.txt", ec);
if (ec) {
std::cerr << "错误: " << ec.message() << "\n";
}
// 所有 filesystem 函数都有 error_code 重载
fs::create_directories("/tmp/a/b/c", ec);
auto size = fs::file_size("file.txt", ec);实用工具函数
cpp
// 临时目录
fs::path tmp = fs::temp_directory_path(); // /tmp 或 %TEMP%
// 磁盘空间
auto space = fs::space("/");
std::cout << "总空间: " << space.capacity / 1e9 << " GB\n";
std::cout << "可用空间: " << space.available / 1e9 << " GB\n";
// 相对路径
fs::path base = "/home/user";
fs::path target = "/home/user/docs/file.txt";
fs::path rel = fs::relative(target, base); // "docs/file.txt"
// 绝对路径
fs::path abs = fs::absolute("relative/path");
// 实用:确保目录存在
void ensure_dir(const fs::path& dir) {
std::error_code ec;
fs::create_directories(dir, ec);
// 忽略"已存在"错误
}
// 实用:安全写文件(先写临时文件,再重命名)
void safe_write(const fs::path& target, const std::string& content) {
auto tmp = target;
tmp += ".tmp";
{
std::ofstream f(tmp);
f << content;
}
fs::rename(tmp, target); // 原子操作(同一文件系统)
}关键认知
<filesystem> 让跨平台文件操作变得简单。路径拼接用 / 运算符,遍历用范围 for。生产代码中用 error_code 重载代替异常,避免异常开销。fs::rename 在同一文件系统上是原子操作,适合安全写文件。