发布于 2026-10-06 · 本文所有数据均为脚本真实运行输出
改完一份产品说明、合同、报告,想知道到底改了哪几行?对方回过来的 Word 只说"小改了几处"?把两版各存成 txt,这个脚本 10 秒列出所有差异行,不用装 Beyond Compare。
# -*- coding: utf-8 -*-
"""比较两个文本文件,输出所有不同行(带行号)。
用法: python3 text_diff.py before.txt after.txt [输出文件] [--keep-ws]
默认忽略行首尾空白和空行(适合对比 Word 导出/复制的文本);
加 --keep-ws 则逐字符严格比较(适合代码、合同条款等缩进有意义的文本)。
"""
import sys, difflib
def read_lines(path):
data = open(path, 'rb').read()
for enc in ('utf-8-sig', 'gbk'):
try:
return data.decode(enc).splitlines()
except UnicodeDecodeError:
continue
return data.decode('utf-8', errors='replace').splitlines()
def norm(lines, keep_ws, ignore_blank):
out = []
for l in lines:
l = l.rstrip('\r\n')
if not keep_ws:
l = l.strip()
if ignore_blank and not l:
continue
out.append(l)
return out
def main(f1, f2, out='diff_result.txt', keep_ws=False):
a = norm(read_lines(f1), keep_ws, not keep_ws)
b = norm(read_lines(f2), keep_ws, not keep_ws)
d = difflib.unified_diff(a, b, fromfile=f1, tofile=f2, lineterm='', n=1)
out_lines = list(d)
with open(out, 'w', encoding='utf-8') as f:
f.write('\n'.join(out_lines) + '\n')
changed = sum(1 for l in out_lines if l[:1] in '+-' and not l.startswith(('+++','---')))
mode = '严格' if keep_ws else '忽略空白'
print(f"[{mode}] 差异 {changed} 行,已写入 {out}")
for l in out_lines:
print(l)
return changed
if __name__ == '__main__':
args = [a for a in sys.argv[1:] if not a.startswith('--')]
keep_ws = '--keep-ws' in sys.argv
f1, f2 = args[0], args[1]
out = args[2] if len(args) > 2 else 'diff_result.txt'
main(f1, f2, out, keep_ws)
构造 before / after 两版文本(改版本号、换电话、删地址、改价格、加新品、加条款):
$ python3 text_diff.py before.txt after.txt
[忽略空白] 差异 9 行,已写入 diff_result.txt
--- before.txt
+++ after.txt
@@ -1,9 +1,10 @@
-产品说明v1
+产品说明v2(已更新)
联系人: 张三
-电话: 13800000000
-地址: 北京市朝阳区某路1号
+电话: 13900000000
价格表
-A 10
+A 12
B 20
+C 33
合同条款
双方协商
+仲裁
每处改动一行 -/+,未变的行原样带出上下文。diff_result.txt 直接发群里或存档。
把 after 的行整个倒序存成 after_shuffled.txt 再跑:
$ python3 text_diff.py before.txt after_shuffled.txt diff2.txt
[忽略空白] 差异 17 行
内容"其实一样"但行序不同 → 17 行差异。difflib 是按行序对比的,要"内容集合级"对比得先 sort,两种需求别搞混。
两次运行 diff_result.txt 逐字节相同,脚本无副作用,可放心反复跑。
strip()(实测这类假差异归零);但对比代码/缩进有意义的文本要加 --keep-ws——实测该模式下 2 处空格差异被正确检出。\r\n,不处理的话整份文件"全部不同"。用 splitlines() + rstrip,实测 CRLF 文件与 LF 文件对比差异 0 行,正确。open(..., encoding='utf-8') 读会抛 UnicodeDecodeError(实测复现)。脚本按 utf-8-sig → gbk 顺序尝试解码,实测 GBK 文件与同内容 UTF-8 文件对比差异 0 行。-/+ 行数为准,本页统一以"差异 N 行"口径为准。更多办公自动化实战: 博客合集(50+ 篇) · 服务清单
下单代做 →