直接用 File.ReadAllBytes + SequenceEqual 比对二进制内容最简单可靠,适用于几 MB 以内的小文件;大文件须用 SHA256 流式哈希比对;预筛可先比较 Length 和 LastWriteTimeUtc;禁用 ReadAllText 文本比较。
用 File.ReadAllBytes 直接比对二进制内容最简单
如果两个文件都不大(比如几 mb 以内),直接读成字节数组再用 sequenceequal 比较,逻辑清晰、无依赖、不易出错。
- 适合配置文件、小资源、测试断言等场景
- 注意:
File.ReadAllBytes会一次性加载全部内容到内存,超大文件(如 >500MB)容易触发OutOfMemoryException - 别用
string读取后比较——编码差异(BOM、换行符、UTF-8/UTF-16)会让结果不可靠
var bytes1 = File.ReadAllBytes("a.txt");
var bytes2 = File.ReadAllBytes("b.txt");
bool same = bytes1.SequenceEqual(bytes2);
大文件必须流式哈希比对,推荐 SHA256
超过几十 MB 的文件,哈希是唯一靠谱方案;MD5 虽快但已不安全,SHA1 同理;SHA256 是当前 .NET 默认推荐的平衡点(安全性+性能)。
-
SHA256.Create()返回的对象不能复用,每次计算新文件都要新建实例 - 别手动拼接
HashAlgorithm.ComputeHash(stream)结果字符串——直接比对byte[]更安全(避免大小写、前导零等问题) - 注意流位置:传入的
FileStream必须从Position == 0开始,否则哈希值错误
using var sha = SHA256.Create();
using var fs1 = new FileStream("a.bin", FileMode.Open, FileAccess.Read, FileShare.Read, 8192, FileOptions.SequentialScan);
using var fs2 = new FileStream("b.bin", FileMode.Open, FileAccess.Read, FileShare.Read, 8192, FileOptions.SequentialScan);
bool same = sha.ComputeHash(fs1).SequenceEqual(sha.ComputeHash(fs2));
用 File.GetLastWriteTimeUtc 和 FileInfo.Length 做快速预筛
哈希计算再快也有开销。如果只是做「高频小变动」检测(比如监控目录下文件是否被修改),先比大小和时间戳能跳过 90% 以上的哈希计算。
-
FileInfo.Length是元数据,毫秒级;File.GetLastWriteTimeUtc也极快,且 Windows/Linux/macOS 都支持 - 注意:仅靠时间戳不可靠——手动改时间、NFS 时钟不同步、某些编辑器会先删后写,导致时间相同但内容不同
- 建议组合使用:
Length不同 → 必不同;Length相同但时间不同 → 很可能不同;都相同 → 再走哈希
别踩 File.ReadAllText + == 这个坑
这是新手最常写的“看起来很合理”的代码,但实际几乎总是错的。
- 文本文件换行符(
\r\nvs\n)会被统一处理,导致原始差异丢失 - BOM(Byte Order Mark)在 UTF-8 文件里可能被
ReadAllText自动剥离,而另一个文件没 BOM,结果误判为不同 - 空格、制表符、不可见控制字符在不同编码下解析行为不一致
- 哪怕你指定了
Encoding.UTF8,也不能保证源文件真实编码就是 UTF-8
除非你 100% 控制两端文件的生成方式、编码、换行规范,并且只比对人工编辑的纯文本,否则一律按二进制或哈希处理。
哈希值相等不代表绝对一致(理论上存在碰撞),但 SHA256 在实际工程中可视为等价;真正难处理的是「部分写入未完成」的文件——比如程序崩溃后残留的半截日志,此时长度和哈希都会错,得结合业务加校验机制。










