用 diff 命令行工具比纯 Python 更快,尤其适合大文件比对;difflib 仅适用于小文件调试或单元测试,性能差且易内存溢出。

用 diff 命令行工具比纯 Python 更快,但需注意路径和编码
直接调用系统 diff 是最轻量、最可靠的方式,尤其对大文件(几十 MB 以上)。Python 自带的 difflib 在内存和性能上都不适合真实场景下的快速比对。
关键点:
-
diff默认按行比较,不解析二进制内容;若需对比二进制文件,得加-a强制文本模式,或改用cmp - Windows 用户需确认
diff可用(Git for Windows 自带,或安装diffutils),否则会报FileNotFoundError: [WinError 2] 系统找不到指定的文件 - 中文路径或含空格路径必须用双引号包裹,否则 shell 调用失败
- 编码不一致会导致误报差异,建议统一用
utf-8读写,或用iconv预处理
示例(生成 unified diff 补丁):
import subprocess
result = subprocess.run(
['diff', '-u', 'old.txt', 'new.txt'],
capture_output=True,
text=True,
encoding='utf-8'
)
patch_content = result.stdout # 成功时 stdout 是补丁,stderr 为空;无差异时 stdout 为空
difflib.unified_diff() 适合小文件调试,但别用于生产补丁生成
它不依赖外部命令,纯 Python 实现,适合单元测试或临时脚本中做逻辑验证。但它把整个文件读入内存、逐行哈希比对,对 >10MB 的文件容易卡顿甚至 OOM。
立即学习“Python免费学习笔记(深入)”;
常见陷阱:
- 输入必须是字符串列表,且每行末尾要带换行符(
\n),否则unified_diff会漏掉最后一行或格式错乱 - 默认不忽略空白差异,
lineterm=''参数不能省,否则补丁里多出多余空行 - 不保留原始文件的编码元信息,生成的补丁无法直接被
patch命令应用(缺少--- /dev/null或时间戳等头信息)
最小可用示例:
import difflib
with open('old.txt', encoding='utf-8') as f:
a = f.readlines()
with open('new.txt', encoding='utf-8') as f:
b = f.readlines()
# 注意:readlines() 保留 \n,无需额外添加
diff_lines = list(difflib.unified_diff(a, b, fromfile='old.txt', tofile='new.txt', lineterm=''))
patch = ''.join(diff_lines)生成可被 patch 命令直接应用的补丁,必须满足三要素
哪怕内容正确,缺一不可,否则 patch -p1 会报 <code>malformed patch 或 no file to patch。
三要素是:
- 第一行必须是
--- old.txt(源文件路径),第二行是+++ new.txt(目标文件路径),路径名需与实际一致(相对/绝对路径影响-p参数行为) - 每段差异前要有
@@ -X,Y +A,B @@行,其中X和A是起始行号,Y和B是上下文行数;用difflib时该行由库自动生成,但用subprocess调diff时默认就有 - 补丁内容必须以 Unix 换行符(
\n)结尾,Windows 的\r\n会导致patch解析失败
验证方式:生成后执行 file patch.diff(Linux/macOS)或检查是否含 ^M(Windows 编辑器中),必要时用 dos2unix 处理。
大文件增量比对?别碰 difflib.SequenceMatcher
它底层是 O(N²) 动态规划算法,10 万行文本就可能耗时数秒、吃掉几百 MB 内存。真有“只比新增/删除块”需求,应转向更底层思路:
- 对文件按块哈希(如每 1024 字节算一个
xxh3_64),用集合求差,定位变动块范围,再在局部用diff细比 —— 这才是“快速”的本质 - 若需跨机器同步,直接上
rsync协议(Python 可调subprocess.run(['rsync', ...])),它内置滚动校验与 delta 传输,比手写补丁更稳 - Git 用户优先走
git diff --no-index old.txt new.txt > patch.diff,它自动处理换行、编码、二进制安全等边界情况
补丁不是目的,让变更可复现、可逆、可审计才是。所以生成后务必用 patch --dry-run -f 验证一次,哪怕多花 0.1 秒。


















