← 中文资源

简体中文版本

基于 LCS 的逐行差异比较

用最长公共子序列比较两段多行文本,按顺序返回相等、删除和插入操作。

阅读英文原文

功能说明

diffLines 把两段多行文本拆成行数组,构造最长公共子序列(LCS)长度表,再回溯得到逐行操作序列。每个操作带有 type 和 line,类型为 equal、delete 或 insert;按顺序消费这些操作即可渲染行内差异、统计变更或生成补丁。它适合命令行工具的文本比较、配置审计和测试快照对比。

实现统一处理 CRLF 和 LF,并忽略文件末尾换行造成的空行差异。diffStats 汇总三类操作数量,方便快速判断两段文本是否有变化。整个实现只依赖 JavaScript 内置的 Int32Array,不需要第三方 diff 库。

关键代码

动态规划从右下角向左上角填充 LCS 长度;回溯时若当前行相等就输出 equal,否则比较下方和右方的长度,选择删除或插入。

table[row][column] =
  left[row] === right[column]
    ? table[row + 1][column + 1] + 1
    : Math.max(table[row + 1][column], table[row][column + 1]);

回溯结束后,任一数组仍有剩余行时会追加对应的删除或插入操作,保证所有输入行都出现在结果中。

使用注意

该算法的时间复杂度和空间复杂度都是 O(n*m),适合数百到数千行的文本;对超大文件应改用 Myers 等更节省空间的算法,或先按块比较。表中使用 Int32Array 存储长度,行数极大时仍可能占用较多内存。重复行较多时 LCS 可能有多个等价解,本实现固定优先删除,因此差异结果不一定是最小编辑脚本。

splitLines 会移除末尾的空字符串,但保留文本中间的空行。只有换行符不同的两段文本会被视为相同。diffStats 遇到未知操作类型会抛出 TypeError,可作为内部一致性检查。操作中的 line 是原始文本行,不包含换行符;渲染时需要自行补回。

验证方式

示例比较 alpha/beta/gamma/delta 与包含 beta changed 和新增 epsilon 的版本,输出应依次为相等 alpha、删除 beta、插入 beta changed、相等 gamma、相等 delta、插入 epsilon,统计结果为 { equal: 3, delete: 1, insert: 2 }。可以再把 CRLF 版本与 LF 版本比较,确认没有产生差异。