文本逐行对比
并排找出两版文本的新增与删除行
文本对比的原理:LCS 算法、行级比较与噪声处理
为什么按行比,而不是按字符比
字符级对比在一行内容改动一个字时,能精确显示是哪几个字变了;但在代码、配置、CSV 这类结构化文本上,字符级会出现大量无意义的碎片——把行尾的一个逗号改成换行,字符 diff 会标出整行被改,而人想看的是"这一行变了"。
行级对比是工程实践中的默认选择:每一行作为一个不可分割的单元来比较,输出"哪些行删除了、哪些行新增了、哪些行保持不变"。这与 git diff、diff -u、代码评审界面用的都是同一套粒度。
LCS:怎么算出"最少改几行"
行级 diff 的核心是最长公共子序列(Longest Common Subsequence)。思路是:找出两个版本共同拥有的、顺序一致的最长行序列,这些行就是"没变的部分";剩下的行就是插入或删除。
举例,原文三行 A B C,新文三行 A C D:
| 结果 | 解释 | |
|---|---|---|
| 公共子序列 | A C | 顺序一致且都出现,长度 2,这是最长的 |
| 删除 | B | 只在原文里 |
| 新增 | D | 只在新文里 |
算法上用动态规划填一张 n×m 的表(n、m 是两个版本的行数),然后回溯找出这个子序列。所以计算量是行数的乘积级:两个 1000 行的文件需要百万级次比较,很快;两个 100000 行的文件就是百亿级,会明显变慢。工具的表格法实现正是这个复杂度,处理上万行的文件没问题,再大建议改用 git diff 或专门的 diff 库(它们通常加了块级预筛选来跳过未变区域)。
它无法判断"移动"
这是 LCS 类算法的固有局限:把一个段落从文件开头移到结尾,会被报告成"在开头删除"加"在结尾新增",而不是"移动"。
后果是重构类的改动(调整函数顺序、整理配置分组)看起来像大改,而实际上逻辑没变。有些高级 diff 工具会做移动检测(把相同的块配对起来标注为移动),但那需要额外的启发式判断,也可能误判。看到大段红色加绿色时,先判断是不是纯粹的顺序调整。
噪声来源与处理
| 噪声 | 症状 | 处理 |
|---|---|---|
| 行尾空白差异 | 整行被标记为改动,肉眼看不出区别 | 比较前统一去掉行尾空白 |
| 制表符与空格混用 | 缩进看起来一样,实际不同 | 统一缩进字符,或用忽略空白的比较模式 |
| 换行符差异 | 整个文件全部标记为不同 | CRLF(Windows)与 LF(Unix)必须统一 |
| 文件编码差异 | 中文全部乱码或全部标红 | 确认两边都是 UTF-8(BOM 有无也要一致) |
| 重新格式化 | 逐行都不同但内容等价 | 先跑一遍格式化再比,或比较格式化后的结果 |
第三行是最容易踩的:一个用 Windows 编辑器保存、另一个在 Linux 上导出,两边逐行内容完全相同但行尾各带一个 \r,结果是全文件标红——这种情况看起来像"什么都被改了",实际只是换行符。工具会按行内容精确比较,所以遇到"全红但看不出区别"时,先检查换行符。
几个实用做法
- 比较前先规范化。统一换行符、去掉行尾空白、统一缩进。这一步能消掉绝大多数假差异,让真正的改动凸显出来。
- 结构化数据先排序再比。JSON、CSV、properties 这类数据的行顺序不携带语义(或可以约定排序),先排序再比较,差异会从"几百行"降到"几行"。
- 分批处理大文件。先按功能块或时间段切分,再逐块比。一次看 5000 行的差异没有意义,人眼无法判断。
- 记住 diff 是双向的。红色(删除)不一定错,绿色(新增)不一定对——判断依据是业务意图,不是颜色。
- 用于配置核对。两个环境的配置差异用这个方式比对最直观,能直接看出"生产环境多了一个开关"这类问题。
工具怎么用
- 左边放原始(旧)文本,右边放对比(新)文本,每行一条。
- 点对比,输出会标记出删除行、新增行和相同行三类。
- 图例在按钮下方,颜色与含义对应。
比较在浏览器本地完成,两个版本的文本都不会被上传。这对比较含有客户数据、密钥或内部配置的文件是必要的前提。
常见问题
对比算法是什么?
基于最长公共子序列(LCS)的逐行 diff,能稳定找出新增、删除与相同的行,适合版本对比。
为什么有些行被标记为删除而不是修改?
本工具按整行比较,一行内容有变化就视为"旧行删除 + 新行新增",不会做行内字符级比对。