c++oding="utf-8" ?>
std::filesystem::file_size 对目录调用必抛 filesystem_error,因标准明确禁止;须先 is_regular_file(p) 过滤,再调用;对目录递归、符号链接默认跳过;权限等错误需捕获并警告。

为什么 std::filesystem::file_size 会抛出 std::filesystem::filesystem_error
直接遍历目录时对每个 path 调用 file_size() 是常见错误起点。它对目录本身不返回大小(POSIX 下目录是特殊文件,但 C++ 标准库明确要求对目录调用 file_size() 必须抛异常),而你很可能没做 is_regular_file() 判断就莽撞调用了。
实操建议:
- 永远先用
is_regular_file(p)过滤,再对真文件调用file_size(p) - 对
is_directory(p)的路径,递归进入;对is_symlink(p),默认跳过(du默认不跟随符号链接,加-L才跟) - 捕获
filesystem_error并检查.code().value():常见值如EACCES(权限不足)、ENOTDIR(路径中途有非目录项)——这些应记录警告但不中断统计
如何避免递归爆栈或重复统计硬链接
du 实际用 stat.st_dev 和 stat.st_ino 去重,C++ 可用 std::filesystem::status(p).permissions() 不行,得用底层 stat。Windows 下需用 GetFileInformationByHandle,跨平台推荐直接调 stat 系统调用(POSIX 头)。
实操建议:
- 维护一个
std::set<:pair ino_t>></:pair>记录已统计的 inode - 对每个路径,先
stat(p.c_str(), &st),检查st.st_nlink > 1且(st.st_dev, st.st_ino)已存在,则跳过累加 - 递归深度控制:用显式栈(
std::stack)替代函数递归,避免大目录导致栈溢出;每层入栈前检查is_directory()和可读性
怎么让输出格式接近真实 du -sh 的人类可读单位
C++ 没有内置 human_readable 格式化,但逻辑极简单:从字节数出发,按 1024 倍率除到 或只剩 KiB 单位为止。注意 <code>du 默认用 KiB(不是 KB),且四舍五入到小数点后一位(-h),-sh 则只显示总计。
实操建议:
- 写一个内联函数:
std::string format_size(size_t bytes),按B → KiB → MiB → GiB分段处理 - 用
std::floor(bytes * 10.0 / divisor) / 10.0实现一位小数(避免std::round引入浮点误差) - 不要用
std::ostringstream拼接——小字符串用std::to_string++更快;单位字母大写(KiB,不是kb)
为什么你的程序比 du 慢几倍
关键不在算法,而在 I/O 模式:du 用 getdents()(Linux)或 FindFirstFileEx(Windows)批量读取目录项,而 std::filesystem::directory_iterator 在某些标准库实现(如 libstdc++)中每步都 stat 一次,造成大量重复系统调用。
实操建议:
- Linux 下改用
opendir()/readdir()+ 手动拼路径 + 单次statat(AT_FDCWD, ...),减少路径解析开销 - 禁用
std::filesystem::directory_options::follow_directory_symlink(默认就是 false,但显式写上更安全) - 统计时用
std::atomic<size_t></size_t>累加,避免锁竞争;最终结果再转成size_t输出
硬链接去重和批量目录读取这两点,漏掉任一个,性能就稳输原生 du 三倍以上。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











