filecmp.cmp() 默认 shallow=True 只比大小和修改时间,需显式设 shallow=False 才逐字节比对;cmpfiles() 要求同级目录和文件名列表;大文件建议用 hashlib 分块哈希;dircmp 需触发比对逻辑才能获取 same_files。

filecmp.cmp() 默认只比大小和修改时间,不是内容比对
很多人用 filecmp.cmp() 发现两个文件明明内容不同却返回 True,问题就出在这儿:它默认开启 shallow=True,只检查 os.stat() 的几个字段(比如大小、mtime),不读文件内容。尤其在 NFS、容器挂载、Git 仓库或某些同步工具下,mtime 可能被重置或延迟更新,导致误判。
实操建议:
- 务必显式传
shallow=False才做逐字节内容比对 - 如果文件很大(>100MB),
shallow=False会读取全部内容,内存和速度都受影响 - Windows 上注意换行符差异:文本文件若一方是
\r\n、另一方是\n,filecmp.cmp(..., shallow=False)仍会返回False——它不做规范化,纯二进制比
示例:
import filecmp
# ❌ 可能误判
print(filecmp.cmp('a.txt', 'b.txt')) # 默认 shallow=True
<h1>✅ 真正比内容</h1><p>print(filecmp.cmp('a.txt', 'b.txt', shallow=False))
filecmp.cmpfiles() 对目录批量比对时路径必须同级
filecmp.cmpfiles() 常被用来比两个目录里「同名文件」的内容,但它要求传入的是两个**目录路径 + 一个共同的文件名列表**,且它不会自动递归,也不会帮你找哪些文件名重合。最容易踩的坑是:给它传了绝对路径或不同层级的相对路径,结果全报 OSError: [Errno 2] No such file or directory。
立即学习“Python免费学习笔记(深入)”;
实操建议:
- 两个目录参数必须是字符串路径(如
'dir1','dir2'),不能是Path对象(除非转成字符串) - 第三个参数是文件名列表,比如
['config.json', 'data.csv'],不是完整路径 - 它只比这个列表里的文件,不会自动扫描目录;漏掉的文件不会出现在结果里
- 返回三元组
(match, mismatch, errors),其中errors包含任一目录里缺失该文件的情况
示例:
import filecmp
# ✅ 正确:同级目录 + 文件名列表
match, mismatch, errors = filecmp.cmpfiles(
'v1', 'v2', ['main.py', 'README.md']
)
<h1>❌ 错误:传了绝对路径或带子目录</h1><h1>filecmp.cmpfiles('/tmp/a', '/tmp/b', ['sub/x.py']) # 不会自动拼路径</h1><p>大文件比对别硬刚 filecmp,改用 hashlib 分块校验
当文件超过几十 MB,filecmp.cmp(..., shallow=False) 会把整个文件读进内存,容易触发 MemoryError 或拖慢脚本。这不是 bug,是设计使然——filecmp 定位是轻量工具,不是生产级校验方案。
实操建议:
- 用
hashlib.sha256()分块读取(比如每次 8192 字节),避免内存暴涨 - 只要两个文件 hash 一致,内容就 100% 一致;不一致则立刻返回,不用读完
- 注意:文本编码不影响二进制 hash,所以无需 decode;但如果你关心逻辑等价(比如忽略空格/换行),那就得另写解析逻辑
示例:
import hashlib
def file_hash(path):
h = hashlib.sha256()
with open(path, 'rb') as f:
while chunk := f.read(8192):
h.update(chunk)
return h.hexdigest()
<p>print(file_hash('a.bin') == file_hash('b.bin'))
filecmp.dircmp() 的 common_files 和 same_files 容易混淆
filecmp.dircmp 是唯一支持递归比目录的内置方案,但它的属性名很误导人:common_files 表示「两个目录里都存在且名字相同」的文件(不管内容),而 same_files 才是内容一致的那些——但前提是你要先调 dircmp.report() 或访问 left_only/right_only 触发内部比对逻辑,否则 same_files 始终为空。
实操建议:
- 别直接依赖
d.left_list或d.right_list判断文件是否存在,它们不反映实际磁盘状态,而是初始扫描快照 - 要获取真正内容一致的文件,必须调一次
d.report_full_closure()或至少访问d.diff_files,否则same_files不更新 - 它不处理符号链接,默认跳过;想包含链接需手动设置
follow_symlinks=False并自行处理
示例:
import filecmp
d = filecmp.dircmp('src', 'dst')
# ❌ 这时 d.same_files 是空列表
print(d.same_files) # []
<h1>✅ 必须触发比对逻辑</h1><p>d.report() # 或 d.diff_files, d.same_files 等任意访问
print(d.same_files) # 现在才有值
实际用 filecmp 时,最麻烦的不是语法,而是它很多行为依赖隐式状态(比如 dircmp 的惰性计算)、不报错但返回空结果,或者默认策略和直觉相反(比如 shallow=True)。遇到 “明明不一样却说一样”,第一反应不该是换库,而是查参数、看文档里那个小写的 default 是什么。










