在 C++17 标准中,std::filesystem 库为文件和目录的操作提供了统一、跨平台的接口。本文将通过一个完整的示例,展示如何使用 std::filesystem 对文件系统进行遍历、复制、删除、以及获取文件属性等常见操作,并结合异常处理和现代 C++ 的语法特性,让代码既简洁又易维护。
1. 关键头文件与命名空间
#include <iostream>
#include <filesystem>
#include <fstream>
#include <vector>
#include <string>
namespace fs = std::filesystem;
使用 namespace fs = std::filesystem; 可以简化后续代码中对库的引用。
2. 基本文件与目录操作
2.1 判断路径是否存在
fs::path dir{"./example_dir"};
if (!fs::exists(dir)) {
std::cout << "目录不存在,正在创建...\n";
fs::create_directory(dir);
}
2.2 创建多级目录
fs::path nested{"./example_dir/sub1/sub2"};
fs::create_directories(nested); // 若父级不存在则一并创建
2.3 创建临时文件
fs::path temp_file = fs::temp_directory_path() / "demo.tmp";
std::ofstream ofs(temp_file);
ofs << "临时数据\n";
ofs.close();
3. 文件遍历与筛选
使用 recursive_directory_iterator 可以递归遍历目录。
std::vector<fs::path> txt_files;
for (const auto& entry : fs::recursive_directory_iterator(dir)) {
if (entry.is_regular_file() && entry.path().extension() == ".txt") {
txt_files.push_back(entry.path());
std::cout << "找到文本文件: " << entry.path() << '\n';
}
}
4. 复制与移动
fs::path src = dir / "sample.txt";
fs::path dst = nested / "sample_copy.txt";
try {
fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
std::cout << "文件复制成功!\n";
} catch (const fs::filesystem_error& e) {
std::cerr << "复制失败: " << e.what() << '\n';
}
移动(rename)操作同样简便:
fs::rename(src, src.parent_path() / "moved_sample.txt");
5. 删除文件与目录
// 删除单个文件
fs::remove(src);
// 删除非空目录,需使用 recursive_remove_all
fs::remove_all(dir); // 递归删除
6. 文件属性查询
fs::path file = nested / "sample_copy.txt";
if (fs::exists(file)) {
auto perms = fs::status(file).permissions();
std::cout << "权限: " << std::oct << static_cast<unsigned>(perms) << '\n';
auto size = fs::file_size(file);
std::cout << "文件大小: " << size << " 字节\n";
auto last_write = fs::last_write_time(file);
std::time_t cftime = decltype(last_write)::clock::to_time_t(last_write);
std::cout << "上次修改时间: " << std::asctime(std::localtime(&cftime));
}
7. 异常处理与错误码
std::filesystem 的多数函数会在遇到错误时抛出 std::filesystem_error。可以捕获异常,也可以使用 std::error_code 作为第二个参数,来获得错误码而不抛异常。
fs::error_code ec;
fs::copy_file(src, dst, fs::copy_options::overwrite_existing, ec);
if (ec) {
std::cerr << "复制错误: " << ec.message() << '\n';
}
8. 完整示例代码
#include <iostream>
#include <filesystem>
#include <fstream>
#include <vector>
#include <string>
#include <chrono>
namespace fs = std::filesystem;
int main() {
try {
// 1. 准备目录
fs::path base_dir{"./demo_fs"};
fs::create_directories(base_dir / "subdir");
// 2. 创建文件
std::vector<fs::path> files = {
base_dir / "file1.txt",
base_dir / "file2.log",
base_dir / "subdir" / "file3.txt"
};
for (auto& p : files) {
std::ofstream ofs(p);
ofs << "内容示例: " << p.filename() << '\n';
}
// 3. 遍历并复制 .txt 文件
for (const auto& entry : fs::recursive_directory_iterator(base_dir)) {
if (entry.is_regular_file() && entry.path().extension() == ".txt") {
fs::path target = base_dir / "txt_copy" / entry.path().filename();
fs::create_directories(target.parent_path());
fs::copy_file(entry.path(), target, fs::copy_options::overwrite_existing);
std::cout << "复制 " << entry.path() << " 到 " << target << '\n';
}
}
// 4. 删除日志文件
for (const auto& entry : fs::directory_iterator(base_dir)) {
if (entry.is_regular_file() && entry.path().extension() == ".log") {
fs::remove(entry.path());
std::cout << "已删除日志文件: " << entry.path() << '\n';
}
}
// 5. 输出文件属性
for (const auto& entry : fs::recursive_directory_iterator(base_dir)) {
if (entry.is_regular_file()) {
std::cout << entry.path() << " 大小: " << fs::file_size(entry.path()) << " 字节, 修改时间: " << std::chrono::system_clock::to_time_t(
fs::last_write_time(entry.path()).time_since_epoch()) << '\n';
}
}
} catch (const fs::filesystem_error& e) {
std::cerr << "文件系统错误: " << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
9. 小结
std::filesystem提供了对文件系统的统一、高效操作,避免了平台差异导致的繁琐代码。- 通过异常或错误码两种方式可以优雅地处理错误。
- 与 C++17 的其他特性(如
auto、structured bindings、range-for等)结合使用,能写出既简洁又易读的文件系统代码。
下一步,你可以尝试在此基础上实现更复杂的功能,例如:
- 递归删除某一类型的文件;
- 监控目录变更(C++20 开始可用
std::filesystem::file_time_type与系统事件结合); - 编写跨平台的压缩/解压工具,使用
std::filesystem与第三方压缩库配合。
祝你编码愉快!