告别跨平台噩梦:C++17 文件系统库带来的革命性变化filesystem

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';
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值