用 crypto/md5 和 crypto/sha256 计算文件哈希需以只读二进制模式打开文件并完整读取,推荐 os.open + io.copy;算法名须小写标准化并映射校验;多文件需每次新建哈希对象;输出格式应兼容工具链,如“ ”。

怎么用 crypto/md5 和 crypto/sha256 算文件哈希
Go 标准库的哈希接口统一,但容易忽略「文件要以只读二进制模式打开」和「必须完整读取」两个前提。没读完或用了文本模式(比如 os.Open 后直接传给 hash.Write 而不处理 error),结果就错。
实操建议:
- 用
os.Open打开文件,别用ioutil.ReadFile—— 大文件会爆内存 - 用
io.Copy把文件流喂给哈希对象,它自动处理分块和错误传播 - 别自己写循环读
[]byte再Write,容易漏掉最后不足缓冲区大小的一段
file, _ := os.Open("demo.txt")
defer file.Close()
h := md5.New()
io.Copy(h, file)
fmt.Printf("%x\n", h.Sum(nil))
为什么命令行参数解析后要立刻校验算法名
用户输 md5、MD5、sha256、sha-256 都可能,但 crypto/sha256 不认带短横线的字符串,crypto/md5 更不接受大写。不提前 normalize,后面会 panic 或静默返回空值。
常见错误现象:输入 ./hash -a MD5 demo.txt 却输出全零哈希,或者 panic:panic: crypto: requested hash function is unavailable
立即学习“go语言免费学习笔记(深入)”;
实操建议:
- 把输入的算法名转小写,再用
strings.Trim去掉空格和短横线(如sha-256→sha256) - 用 map 映射合法值:
map[string]func() hash.Hash{"md5": md5.New, "sha256": sha256.New},查不到就报错退出 - 别在哈希计算途中才检查算法名,那会浪费 I/O
校验多个文件时,为什么不能复用同一个哈希对象
hash.Hash 接口不是线程安全的,且调用 Sum(nil) 后内部状态不会自动重置。如果对多个文件复用一个 md5.New() 实例,第二个文件的哈希其实是第一个 + 第二个的拼接结果。
使用场景:批量校验目录下所有 .go 文件
实操建议:
- 每次循环都调用一次
md5.New()或sha256.New(),不要提出来做全局变量 - 如果想少写几行,封装成函数:
func newHash(algo string) hash.Hash { ... } - 注意
Sum(nil)返回的是新分配的切片,不用手动清零;但对象本身不能重用
输出格式要不要加前缀(如 md5=)和换行控制
工具最终要被其他脚本消费(比如 grep 或 awk),裸哈希值容易和其他日志混在一起。不加标识、不控制换行,会导致管道处理失败。
性能影响极小,但兼容性差:比如 ./hash a.go b.go | awk '{print $1}' 会把文件名和哈希一起切
实操建议:
- 默认输出格式为
<hash><filename></filename></hash>(两个空格分隔),符合 GNU coreutils 工具链习惯 - 加
-p参数可选输出md5=xxx a.go这种带算法前缀的格式 - 绝对不要用
\r\n,Linux/macOS 下换行必须是\n;文件名含空格时也不额外引号——那是调用方该处理的
哈希计算本身很简单,真正花时间的是路径合法性判断、编码容错(比如文件名含 Unicode)、以及错误提示是否能让用户一眼看出是权限问题还是算法拼错了。










