filecmp.cmp() 是判断大文件内容是否完全相同的最轻量方法,逐块读取、不加载全文到内存,但仅返回布尔值且需确保路径正确、文件未被锁定、编码一致。

用 filecmp.cmp() 快速判断是否完全相同
如果只是想知道两个大文件内容是否一字不差,filecmp.cmp() 是最轻量的选择。它默认逐块读取、不加载全文到内存,适合 GB 级文件。
注意:它只返回 True 或 False,不告诉你哪里不同;且默认启用 shallow=False(比较内容而非仅修改时间/大小),这点必须确认。
- 确保传入的是绝对路径或同级目录下的相对路径,否则可能因工作目录问题误判
- 若文件被其他进程锁定(如 Excel 正在打开 .csv),会抛
OSError: [Errno 13] Permission denied - 二进制文件(如 PDF)也能比,但文本编码差异(如 UTF-8 vs GBK)会导致误报为不同——此时需先统一解码再比
用 diff 命令行工具做行级差异定位
要看到具体哪几行不同,Python 调用系统 diff 最稳。Linux/macOS 自带,Windows 需安装 Git for Windows 或 WSL 后才可用 diff。
别用 subprocess.run(['diff', ...]) 直接捕获输出——大文件可能触发管道阻塞或内存溢出。改用 stdout=subprocess.PIPE + text=True + 分块读取:
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
import subprocess
result = subprocess.run(
['diff', '-u', 'a.txt', 'b.txt'],
capture_output=True,
text=True,
timeout=300 # 防卡死
)
if result.returncode == 1: # 有差异时返回 1,不是错误
print(result.stdout)-
-u输出统一格式,便于解析;-q只报“files differ”但不显示内容,适合脚本判断 - 超大文件(>100MB)建议加
--speed-large-files(GNU diff)减少哈希开销 - Windows 原生命令
fc不支持-u,输出格式难解析,优先换diff
用 difflib.SequenceMatcher 在内存中细粒度比对
当必须纯 Python 实现、且文件能载入内存(比如几十 MB 的日志),difflib.SequenceMatcher 可逐行甚至逐字符对比,还能计算相似度。
但它默认把整行当一个元素,对超长行(如 minified JSON)效率骤降。关键参数是 autojunk=False(禁用自动过滤重复行)和 isjunk 函数:
from difflib import SequenceMatcher
with open('a.txt') as f1, open('b.txt') as f2:
lines1 = f1.readlines()
lines2 = f2.readlines()
matcher = SequenceMatcher(lambda x: x.isspace(), lines1, lines2)
for tag, i1, i2, j1, j2 in matcher.get_opcodes():
print(f'{tag}: a[{i1}:{i2}] -> b[{j1}:{j2}]')-
lambda x: x.isspace()表示空白行不算“垃圾”,避免误删空行导致错位 - 若文件含大量重复行(如模板日志头),设
autojunk=True反而更快,但可能漏掉某些细微差异 - 相似度
ratio()返回 0~1 浮点数,0.95以上通常表示仅有少量增删,不必深挖
处理编码不一致和行尾差异的实际技巧
真实场景中,UnicodeDecodeError 和 \r\n vs \n 才是最常卡住的地方。
不要依赖 open(..., encoding='utf-8') 硬解——先用 chardet 探测编码,再标准化行尾:
import chardet
def read_normalized(path):
with open(path, 'rb') as f:
raw = f.read()
enc = chardet.detect(raw)['encoding'] or 'utf-8'
text = raw.decode(enc).replace('\r\n', '\n').replace('\r', '\n')
return text.splitlines(keepends=False)-
chardet对小文件准,大文件可只读前 10KB:raw[:10000] -
splitlines(keepends=False)统一去掉换行符,避免'line1\n'和'line1'被判不同 - 若文件含 BOM(如
utf-8-sig),decode('utf-8-sig')比先探测更可靠
真正麻烦的永远不是算法,而是编码隐式转换和换行符混用——先标准化再比,省去 80% 的调试时间。

















