应枚举挂载点再调用std::filesystem::space:Linux/macOS解析/proc/mounts并过滤虚拟文件系统,Windows用GetLogicalDrives+GetDiskFreeSpaceEx,跨平台封装需按OS分发并校验路径有效性。

如果您在 C++ 程序中需要准确获取系统中每个硬盘分区的挂载点路径及其对应的用户可用剩余空间,但仅调用 std::filesystem::space 却始终返回根目录或重复数据,则问题根源在于该函数不枚举挂载点,仅对输入路径所在文件系统返回统计信息。以下是解决此问题的多种跨平台方案:
一、使用 std::filesystem::space 配合手动挂载点枚举(Linux/macOS)
该方法适用于 Linux 和 macOS 系统,核心是先从 /proc/mounts 或 /proc/self/mounts 解析真实挂载点列表,再对每个挂载点调用 std::filesystem::space 获取空间数据。必须过滤虚拟文件系统(如 tmpfs、proc、sysfs),且需捕获异常以跳过权限不足或不可访问路径。
1、打开 /proc/self/mounts 文件流,逐行读取;
2、按空格分割每行,提取第二列(挂载点路径)和第三列(文件系统类型);
3、若文件系统类型为 ext4、xfs、btrfs 或 ntfs3,则保留该挂载点;
4、对每个保留的挂载点路径调用 std::filesystem::space(mountpoint);
5、捕获 std::filesystem::filesystem_error 异常并忽略失败项;
6、输出 info.available 字段作为当前用户实际可写入字节数。
二、使用 statvfs 直接查询挂载点(Linux/macOS 原生方案)
该方法绕过标准库抽象,直接调用 POSIX statvfs() 系统接口,能获取更底层、更精确的块级信息(如 f_bavail 和 f_frsize),避免 std::filesystem::space 在旧环境中的兼容性问题。它不依赖 C++20,且结果不受符号链接解析影响。
1、遍历通过 getmntent("/proc/mounts") 获取的挂载点结构体;
2、检查 ent->mnt_type 是否为物理文件系统类型(排除 tmpfs、devtmpfs);
3、调用 statvfs(ent->mnt_dir, &buf),验证返回值是否为 0;
4、若成功,计算可用字节数:buf.f_bavail * buf.f_frsize;
5、总容量计算为:buf.f_blocks * buf.f_frsize;
6、对 ENOENT 或 ENOTBLK 错误使用 strerror(errno) 记录原因。
三、使用 Win32 API 枚举驱动器与卷(Windows 原生方案)
该方法专用于 Windows 平台,利用 GetLogicalDrives() 获取盘符驱动器位掩码,并结合 GetDiskFreeSpaceEx() 获取精确空间数据。它能正确处理 NTFS 配额、卷影复制及回收站策略,且支持无盘符挂载点(需额外调用 FindFirstVolumeW())。
1、调用 GetLogicalDrives() 获取 32 位掩码;
2、对每位 i,构造路径 std::string("A:\") + i(A 对应第 0 位);
3、确保路径末尾含反斜杠,否则 GetDiskFreeSpaceEx() 返回失败;
4、调用 GetDiskFreeSpaceEx(path.c_str(), &free_bytes, &total_bytes, &total_free);
5、其中 free_bytes.QuadPart 即为当前用户可用字节数;
6、若需获取无盘符挂载点(如挂载到 C:\mnt\data),则改用 FindFirstVolumeW() + GetVolumePathNamesForVolumeNameW() 枚举所有路径映射。
四、跨平台封装函数:自动适配当前操作系统
该方案将上述逻辑封装为统一接口,运行时检测操作系统类型并分发至对应实现,避免宏条件编译污染业务逻辑。关键要求是:路径参数必须为真实存在的目录路径;所有异常与 API 错误均需显式处理;返回值单位统一为字节,且优先暴露 available 字段而非 free。
1、调用 std::filesystem::current_path().root_path() 获取当前卷根路径;
2、在 Windows 上,若路径含盘符(如 C:\),则走 Win32 分支;
3、在 Linux/macOS 上,若存在 /proc/self/mounts,则走挂载点解析分支;
4、对每个目标路径,前置校验:std::filesystem::exists(p) && std::filesystem::is_directory(p);
5、执行对应平台的空间查询函数,返回 int64_t 类型的可用字节数;
6、若任一环节失败,返回 -1 并记录错误字符串至日志。
五、规避常见陷阱的关键实践
该方案聚焦于防御性编程,针对知识库中高频出错场景提供硬性约束。例如,不能依赖 std::filesystem::canonical() 处理驱动器路径,也不能用字符串前缀匹配判断子目录归属挂载点——必须使用 std::filesystem::is_subdirectory() 或 std::filesystem::equivalent()。
1、禁止向 std::filesystem::space() 传入空字符串、相对路径(如 "./")或文件路径(如 "/etc/passwd");
2、Linux 下不得用 target_path.string().starts_with(mountpoint) 判断挂载归属,必须用 std::filesystem::is_subdirectory(target_path, mountpoint);
3、Windows 下禁止传入 "C:"(无反斜杠),必须为 "C:\" 或 "C:/"(部分实现接受正斜杠);
4、所有 std::filesystem::space() 调用必须置于 try/catch(const std::filesystem::filesystem_error&) 块内;
5、对 /proc/mounts 中的 none、debugfs、securityfs 类型条目,直接跳过不尝试 space 查询;
6、当 std::filesystem::space() 返回 capacity == 0 || available == static_cast
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











