直接用hashlib.md5(open('file.txt').read())会因一次性加载大文件导致内存溢出或unicode解码错误;正确做法是rb模式分块读取(如8192字节),循环调用update(),最后用hexdigest()获取32位十六进制字符串。

直接用 hashlib.md5() 读文件会出错
很多人一上来就写 hashlib.md5(open('file.txt').read()),结果遇到大文件直接内存爆掉,或者二进制文件被误当文本解码报 UnicodeDecodeError。根本原因在于:MD5 必须基于原始字节计算,且不能一次性把整个文件读进内存。
正确做法是分块读取、逐块更新哈希对象:
- 始终以
'rb'模式打开文件(rb= read binary) - 用固定大小的缓冲区(如
8192字节)循环读取 - 每次调用
hash_obj.update(chunk),而不是传整个文件内容
示例代码片段:
import hashlib
def calc_md5(filepath):
hash_obj = hashlib.md5()
with open(filepath, 'rb') as f:
for chunk in iter(lambda: f.read(8192), b''):
hash_obj.update(chunk)
return hash_obj.hexdigest()
为什么不用 hashlib.md5().update() 一次传整个 bytes?
因为 os.path.getsize() 显示文件才几 MB,不代表它能安全转成 Python bytes 对象——Windows 上某些路径或特殊字符可能导致 open().read() 返回异常长度;更关键的是,32 位 Python 进程对单个对象有约 2GB 内存限制,而实际中几百 MB 的日志或镜像文件就很常见。
分块读取还能带来两个隐性好处:
- 避免因磁盘 I/O 阻塞导致主线程长时间无响应(尤其在 GUI 或 Web 后端里)
- 兼容只读挂载、网络文件系统(如 NFS)等不支持随机读取全部内容的场景
校验时要注意 hexdigest() 和 digest() 的区别
很多人拿到结果后和别人给的 MD5 值比对不上,八成是混淆了这两个方法:
-
hash_obj.hexdigest()返回 32 位小写十六进制字符串(如'd41d8cd98f00b204e9800998ecf8427e'),这是标准校验用途 -
hash_obj.digest()返回 16 字节原始 bytes(如b'\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\t\x98\xec\xf8B~'),一般只用于加密协议内部拼接,不能直接和文本型 MD5 值比较
如果你看到类似 TypeError: Unicode-objects must be encoded before hashing,大概率是误把字符串传给了 update(),而没做 .encode();但读文件时用 'rb' 就天然规避了这个问题。
需要支持超大文件或生产环境?加个进度提示和异常防护
纯脚本跑没问题,但放到自动化流程里就得考虑鲁棒性:
- 文件不存在 → 捕获
OSError或FileNotFoundError - 权限不足 →
PermissionError要明确提示,别静默失败 - 想看进度?可以在循环里用
os.stat(filepath).st_size算百分比,但注意不要每轮都stat,太伤性能
真正容易被忽略的一点是:某些 NAS 或容器挂载卷会在文件被写入时返回临时 size(比如显示为 0),此时 iter(lambda: f.read(...), b'') 仍能正常工作,因为它依赖的是读到空 bytes 判定结束,而不是依赖文件总大小。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











