先用os.Stat和os.SameFile快速判断是否同一文件,是则直接相等;否则先比大小筛除90%差异,再对同大小文件流式计算sha256哈希比对,严禁os.ReadFile加载大文件。

直接比大小再比内容,90% 场景下足够快且可靠;大文件必须流式哈希,别用 os.ReadFile 全量加载。
先用 os.Stat 和 os.SameFile 快速排除
两个路径可能指向同一文件(硬链接或相同 inode),这时根本不用读内容。调 os.Stat 拿到 os.FileInfo 后,用 os.SameFile(fi1, fi2) 判断——返回 true 就直接认定相等。
- 漏掉这步,可能对同一个文件做两次哈希,浪费 I/O 和 CPU
-
filepath.Abs再比路径字符串不可靠:软链接、大小写、./前缀都会绕过 - Windows 下
os.SameFile依赖VolumeSerialNumber+FileIndex,比 Unix 的 inode 更健壮
小文件用 os.ReadFile + bytes.Equal
适用于 ≤10MB 的文本或二进制文件。它自动打开、读取、关闭,不泄露 fd,返回的 []byte 可直接喂给 bytes.Equal —— 切片不能用 ==,这是常见编译错误或静默逻辑错。
- 别转
string再比:二进制文件会 panic,UTF-8 解码失败 - 错误类型是
*os.PathError,需用os.IsNotExist(err)区分“文件不存在”和“读取失败” - 换行符差异(
\r\nvs\n)会导致字节不等,但业务上可能算“相同”——此时要预处理,不是改比对逻辑
大文件必须流式计算 sha256 哈希
超过 100MB 就别碰 os.ReadFile。用 os.Open + io.Copy + sha256.New() 流式计算,内存恒定在几 KB,且支持早期退出(任一文件读失败就立刻返回错误)。
立即学习“go语言免费学习笔记(深入)”;
-
sha256.Sum([]byte)是为小切片设计的,对文件用等于先ReadFile再哈希,完全没规避 OOM - 哈希结果是
[32]byte数组,可直接用==比较,安全且无时序攻击风险 -
fmt.Sprintf("%x", sum)打印的是结构体字段,要用hex.EncodeToString(sum[:])或sum.Sum(nil) - 空文件的 SHA256 固定值是
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,可短路判断
需要知道“哪里不同”时,别自己实现 diff
纯哈希只回答“是否一样”,不回答“怎么不一样”。文本文件优先用 github.com/pmezard/go-difflib,它输出标准 unified diff 格式,稳定、轻量、无 C 依赖。
- 输入必须是
[]string,常见错误是传string(content)而不是strings.Split(string(content), "\n") - Windows 换行符
\r\n会让空行变多,建议预处理统一成\n - 二进制文件别喂给它——会把整个文件当单行字符串拆,OOM 或卡死;改用系统
cmp或fc /b - 若需忽略空白或大小写,必须在调
Difflines前对两个字符串切片做相同预处理,不能靠 diff 库后过滤
真正容易被忽略的不是算法选型,而是边界:硬链接检测、空文件处理、换行符归一化、以及——当 io.Copy 返回 n == 0, err == nil 时,到底是空文件还是读到了 EOF 边界,得结合 os.Stat 的 Size() 判断。


















