直接用文件名去重不可靠,因真正需删除的是内容相同的文件而非名称重复的文件;哈希(如MD5)可精准识别内容重复,分块流式计算避免大文件内存溢出,辅以哈希碰撞校验和硬链接识别确保准确去重。

为什么直接用文件名去重根本不可靠
文件名重复只是表象,真正要删的是内容相同的文件。比如 report_v2_final_copy.txt 和 backup.txt 可能完全一样,但按名字谁也不会认为该删。反过来,data.log 和 data_20241025.log 名字不同,但若都是同一程序刚生成的副本,就得合并。哈希是唯一能绕过命名干扰、直击内容本质的手段。
选什么哈希算法:MD5 还是 SHA-256?
对去重场景,MD5 足够安全——不是做密码学签名,而是防偶然碰撞。SHA-256 计算开销高约 2.5 倍,IO 时间占大头时这点 CPU 差异几乎不可感,但代码更重、哈希值更长(影响内存占用)。除非你处理的是科研级数据或审计要求明确禁止 MD5,否则用 MD5 更实际。
- Windows 下可用
CryptStringToBinary+CryptHashData,但太重;推荐轻量封装如openssl/md5.h - Linux/macOS 直接用
openssl/md5.h或 C++23 的<mdspan></mdspan>配合自定义 hasher(暂不成熟),现阶段仍以 OpenSSL 最稳 - 别手写 MD5 循环——边界错一位、字节序搞反、padding 漏一步,哈希值就全错,且极难调试
怎么避免读完整个大文件才计算哈希
遇到几个 GB 的视频或数据库文件,等它读完再哈希,内存爆、时间长、用户以为卡死。正确做法是分块哈希(streaming hash):每次读 64KB(65536 字节),喂给哈希上下文,最后取最终摘要。这样内存恒定在百 KB 级,且能边读边算。
示例关键逻辑:
立即学习“C++免费学习笔记(深入)”;
MD5_CTX ctx;
MD5_Init(&ctx);
std::ifstream file(path, std::ios::binary);
char buf[65536];
while (file.read(buf, sizeof(buf)) || file.gcount() > 0) {
MD5_Update(&ctx, buf, file.gcount());
}
unsigned char digest[MD5_DIGEST_LENGTH];
MD5_Final(digest, &ctx);
// 转为 32 字符 hex 字符串存入 unordered_map
哈希冲突和硬链接怎么处理
unordered_map<string vector>></string> 存哈希→路径列表,看似简单,但两个问题常被忽略:
- 哈希相同 ≠ 内容相同:极小概率碰撞(MD5 理论碰撞率 ~1/2^128),但生产环境建议对哈希相等的文件做一次
memcmp逐字节比对(只比首尾各 1KB + 总大小,99.9% 情况秒出结果) - 硬链接共享 inode:Linux/macOS 下用
stat().st_ino和stat().st_dev双判,若相同,直接跳过哈希计算——它们本就是同一个数据块,不是“重复”,是“同一份” - Windows 没 inode,但可用
GetFileInformationByHandle取dwVolumeSerialNumber+nFileIndexLow/High组合判断硬链接
真正麻烦的是那种改了 1 字节但哈希全变的文件——它们不该去重,而你的哈希表也自然不会把它们归到一起。这点反而是哈希的“优点”:严格,不滥删。



















