0 老式的跨平台代码
0.1 老式跨平台代码:
// 😱 老式的跨平台代码 - 需要大量的条件编译
#ifdef _WIN32
CreateDirectory("新建文件夹", NULL); // Windows API 创建目录
std::string path = "C:\\Users\\xiaowang\\docs\\test.txt"; // 😫 Windows下需要双反斜杠
#else
mkdir("新建文件夹", 0755); // 🐧 Linux/Unix 系统调用
std::string path = "/home/xiaowang/docs/test.txt"; // 🌟 Unix风格的路径
#endif
0.2遍历目录的代码:
// 😱 传统的跨平台目录遍历代码 - 需要大量条件编译
#ifdef _WIN32
// 🪟 Windows 平台专用代码
WIN32_FIND_DATA findData; // 存储文件信息的结构体
HANDLE hFind = FindFirstFile("C:\\temp\\*", &findData); // 开始搜索第一个文件
if (hFind != INVALID_HANDLE_VALUE) { // ✅ 检查句柄是否有效
do {
std::cout << findData.cFileName << '\n'; // 📝 输出文件名
} while (FindNextFile(hFind, &findData)); // ➡️ 继续查找下一个文件
FindClose(hFind); // 🔒 关闭查找句柄,避免资源泄露
}
#else
// 🐧 Linux/Unix 平台专用代码
DIR* dir = opendir("/tmp"); // 打开目录,获取目录流
struct dirent* entry; // 目录项结构体
while ((entry = readdir(dir)) != NULL) { // 🔄 循环读取每个目录项
std::cout << entry->d_name << '\n'; // 📝 输出文件名
}
closedir(dir); // 🔒 关闭目录流,释放资源
#endif
一 C++17的解决方案
1.1基础用法:
#include <filesystem>
namespace fs = std::filesystem; // 🔧 引入命名空间别名,让代码更简洁
// 📁 基础文件操作
fs::create_directory("新建文件夹"); // ✨ 创建目录,不再需要平台判断
1.2创建目录:
// 🛣️ 智能路径处理
fs::path userPath = fs::current_path() / "docs" / "test.txt";
// 💡 解释:
// - current_path() 获取当前工作目录
// - 使用 / 运算符自动处理不同平台的路径分隔符
// - Windows上会自动转换为反斜杠
std::cout << "标准化路径: " << userPath << '\n';
1.3遍历目录:
// 📂 目录遍历示例
for(const auto& entry : fs::directory_iterator("新建文件夹")) {
// 🔍 获取每个文件/目录的信息
std::cout << entry.path().filename() << '\n'; // 仅输出文件名
// 📊 文件属性查询
if(fs::is_regular_file(entry)) { // ✅ 检查是否为普通文件
auto fileSize = fs::file_size(entry); // 📏 获取文件大小
std::cout << "大小: " << fileSize << " bytes\n";
auto lastWrite = fs::last_write_time(entry); // ⏰ 最后修改时间
// 时间格式化需要额外处理
}
}
1.4 文件复制操作:
// 🔄 文件复制操作
fs::copy("源文件.txt", "备份.txt",
fs::copy_options::update_existing);
// 💡 update_existing 选项的作用:
// - 仅在源文件比目标文件新时才进行复制
// - 避免不必要的文件复制操作
// - 适合增量备份场景
1.5 目录删除操作:
// 🗑️ 递归删除目录
std::uintmax_t deleted = fs::remove_all("临时文件夹");
// 💫 remove_all 的特点:
// - 递归删除目录及其所有内容
// - 返回实际删除的文件数量
// - 自动处理权限和子目录
1.6 文件状态检查:
// 📂 文件状态检查 - 推荐的检查顺序
if(fs::exists("config.json")) {
// ✨ 先检查文件存在性,避免后续操作出错
if(fs::is_regular_file("config.json")) {
// 🔍 进一步确认文件类型
// - 不是目录
// - 不是符号链接
// - 不是特殊文件
std::cout << "是个普通文件呢!" << '\n';
}
}
1.7 获取磁盘信息:
// 💾 磁盘空间查询
fs::space_info si = fs::space("C:");
// 📊 space_info 包含三个关键信息:
std::cout << "总容量: " << si.capacity << " bytes\n" // 💿 磁盘总大小
<< "空闲: " << si.free << " bytes\n" // 🆓 系统级空闲空间
<< "可用: " << si.available << " bytes\n"; // ✅ 当前用户可用空间
1.8 文件操作异常处理:
try {
// 🔄 文件重命名操作
fs::rename("旧文件.txt", "新文件.txt");
// 💡 fs::rename 会自动:
// - 处理跨平台的路径差异
// - 处理文件系统权限
// - 确保操作的原子性
}
1.9 错误处理:
catch(const fs::filesystem_error& e) {
// ⚠️ filesystem_error 包含了详细的错误信息
std::cerr << "哎呀,出错啦:" << e.what() << " 😅\n";
// 🔍 常见错误原因:
// - 📝 文件被锁定:其他程序正在使用
// - 🚫 权限不足:需要管理员权限
// - ❓ 文件不存在:源文件已被删除
// - 💭 无法覆盖:目标文件已存在
}
1.10 获取详细错误信息:
catch(const fs::filesystem_error& e) {
// 🎯 获取具体的路径信息
std::cerr << "源文件:" << e.path1() << '\n' // 📂 第一个相关路径
<< "目标文件:" << e.path2() << '\n'; // 📂 第二个相关路径
// 💡 提示:
// - path1() 和 path2() 可能返回空路径
// - 具体返回什么取决于发生错误的操作类型
}
二 文件系统库的核心概念
2.1基础设置:
namespace fs = std::filesystem; // 🔧 使用命名空间别名
// 💡 这样可以:
// - 避免重复写长名字
// - 让代码更简洁易读
// - 保持与标准库一致的命名风格
2.2 路径操作的基本用法:
fs::path p1 = "foo/bar/config.json"; // 创建路径对象
// 💫 智能路径解析:
fs::path p2 = p1.parent_path(); // 📂 获取父目录: foo/bar
fs::path p3 = p1.filename(); // 📄 获取文件名: config.json
fs::path p4 = p1.extension(); // 🏷️ 获取扩展名: .json
2.3 文件类型判断:
// 📂 文件类型判断
if(fs::is_regular_file(p1)) { // 📄 普通文件检查
std::cout << "这是普通文件" << '\n';
} else if(fs::is_directory(p1)) { // 📁 目录检查
std::cout << "这是目录" << '\n';
} else if(fs::is_symlink(p1)) { // 🔗 符号链接检查
std::cout << "这是符号链接" << '\n';
}
2.4 高级特性:
// 🔗 路径组合与规范化
fs::path base = "/home/user"; // 📍 基础路径
// 🎯 智能路径组合
fs::path full = base / "docs" / "readme.md";
// 💡 使用 / 运算符的好处:
// - 自动处理不同系统的路径分隔符
// - 避免手动拼接字符串
// - 防止双重分隔符
// 🧹 路径规范化
fs::path norm = fs::canonical(full);
// ✨ canonical 函数的功能:
// - 解析所有符号链接
// - 删除 . 和 .. 引用
// - 返回绝对路径
三 高级特性
3.1 递归遍历目录:
// 🌲 递归遍历目录
for(const auto& entry : fs::recursive_directory_iterator("项目目录")) {
// 💡 recursive_directory_iterator的特点:
// - 自动遍历所有子目录
// - 深度优先搜索
// - 自动处理符号链接
if(entry.is_regular_file() && entry.path().extension() == ".cpp") {
std::cout << "找到 C++ 源文件:" << entry.path() << '\n';
}
}
3.2 文件权限管理:
// 🔐 文件权限管理
fs::permissions("脚本.sh",
fs::perms::owner_exec | fs::perms::group_exec, // 👥 设置执行权限
fs::perm_options::add // ➕ 添加而非替换权限
);
// 📝 权限说明:
// - owner_exec: 所有者执行权限
// - group_exec: 用户组执行权限
// - add: 保留现有权限
3.3 系统临时目录的获取:
// 📁 获取系统临时目录
fs::path temp = fs::temp_directory_path();
// 💡 跨平台支持:
// - 🪟 Windows: C:\Users\用户名\AppData\Local\Temp
// - 🐧 Linux: /tmp
std::cout << "系统临时目录:" << temp << '\n';
3.4 检查文件等价性:
// 🔗 文件等价性检查
if(fs::equivalent("a.txt", "链接到a.txt")) {
// ✨ equivalent的强大之处:
// - 自动解析符号链接
// - 处理硬链接
// - 跨平台支持
std::cout << "这是同一个文件!" << '\n';
}
四 错误处理:
4.1 基础设置和文件检查:
try {
// 🗂️ 创建文件路径对象
fs::path p = "大文件.dat";
// ✅ 检查文件是否存在,避免访问不存在的文件
if(fs::exists(p)) {
// 📊 获取文件基本信息
auto fileSize = fs::file_size(p); // 获取文件大小
auto lastWrite = fs::last_write_time(p); // 获取最后修改时间
4.2 文件大小检查和备份路径构建:
// 💾 检查文件大小是否超过100MB
if(fileSize > 1024*1024*100) { // 100MB = 1024*1024*100 bytes
// 🔨 构建备份文件路径
fs::path backup = p.parent_path() / "backup" / p.filename();
// 💡 解释:
// - parent_path():获取父目录
// - "backup":备份子目录名
// - filename():保持原文件名
4.3 实际的备份操作:
// 📁 确保备份目录存在
fs::create_directories(backup.parent_path());
// ✨ create_directories 特点:
// - 可以创建多层目录
// - 已存在则跳过
// - 自动处理权限问题
// 📋 复制文件到备份位置
fs::copy(p, backup, fs::copy_options::overwrite_existing);
// 🎯 copy_options::overwrite_existing 作用:
// - 如果目标文件存在则覆盖
// - 保证备份总是最新的
}
}
4.4 错误处理部分:
} catch(const fs::filesystem_error& e) {
// ❌ 文件系统错误处理
std::cerr << "文件系统错误: " << e.what() << '\n'
<< "路径 1: " << e.path1() << '\n' // 🔍 显示源路径
<< "路径 2: " << e.path2() << '\n'; // 🎯 显示目标路径
// 💢 常见错误类型:
// - 🚫 权限不足
// - 💽 磁盘空间不足
// - 🔒 文件被锁定
} catch(const std::exception& e) {
// 🚨 其他标准异常处理
std::cerr << "其他错误: " << e.what() << '\n';
}