对于更复杂的差异检查,另一种选择是使用 PatienceDiff 算法。我将此算法移植到 Javascript 在...
https://github.com/jonTrent/PatienceDiff
...虽然该算法通常用于逐行比较文本(例如计算机程序),但它仍然可以用于逐字符比较。例如,要比较两个字符串,您可以执行以下操作...
let a = "thelebronnjamist";
let b = "the lebron james";
let difference = patienceDiff( a.split(""), b.split("") );
...difference.lines 被设置为具有比较结果的数组...
difference.lines: Array(19)
0: {line: "t", aIndex: 0, bIndex: 0}
1: {line: "h", aIndex: 1, bIndex: 1}
2: {line: "e", aIndex: 2, bIndex: 2}
3: {line: " ", aIndex: -1, bIndex: 3}
4: {line: "l", aIndex: 3, bIndex: 4}
5: {line: "e", aIndex: 4, bIndex: 5}
6: {line: "b", aIndex: 5, bIndex: 6}
7: {line: "r", aIndex: 6, bIndex: 7}
8: {line: "o", aIndex: 7, bIndex: 8}
9: {line: "n", aIndex: 8, bIndex: 9}
10: {line: "n", aIndex: 9, bIndex: -1}
11: {line: " ", aIndex: -1, bIndex: 10}
12: {line: "j", aIndex: 10, bIndex: 11}
13: {line: "a", aIndex: 11, bIndex: 12}
14: {line: "m", aIndex: 12, bIndex: 13}
15: {line: "i", aIndex: 13, bIndex: -1}
16: {line: "e", aIndex: -1, bIndex: 14}
17: {line: "s", aIndex: 14, bIndex: 15}
18: {line: "t", aIndex: 15, bIndex: -1}
aIndex === -1 或 bIndex === -1 表示两个字符串之间的差异。具体...
- 元素 3 表示在位置 3 的
b 中找到字符“”。
- 元素 10 表示在位置 9 的
a 中找到了字符“n”。
- 元素 11 表示在位置 10 的
b 中找到字符“”。
- 元素 15 表示在
a 的位置 13 中找到了字符“i”。
- 元素 16 表示在位置 14 的
b 中找到了字符“e”。
- 元素 18 表示在位置 15 的
a 中找到字符“t”。
请注意,PatienceDiff 算法对于比较两个相似的文本或字符串块很有用。它不会告诉您是否发生了基本编辑。比如下面...
let a = "james lebron";
let b = "lebron james";
let difference = patienceDiff( a.split(""), b.split("") );
...返回 difference.lines 包含...
difference.lines: Array(18)
0: {line: "j", aIndex: 0, bIndex: -1}
1: {line: "a", aIndex: 1, bIndex: -1}
2: {line: "m", aIndex: 2, bIndex: -1}
3: {line: "e", aIndex: 3, bIndex: -1}
4: {line: "s", aIndex: 4, bIndex: -1}
5: {line: " ", aIndex: 5, bIndex: -1}
6: {line: "l", aIndex: 6, bIndex: 0}
7: {line: "e", aIndex: 7, bIndex: 1}
8: {line: "b", aIndex: 8, bIndex: 2}
9: {line: "r", aIndex: 9, bIndex: 3}
10: {line: "o", aIndex: 10, bIndex: 4}
11: {line: "n", aIndex: 11, bIndex: 5}
12: {line: " ", aIndex: -1, bIndex: 6}
13: {line: "j", aIndex: -1, bIndex: 7}
14: {line: "a", aIndex: -1, bIndex: 8}
15: {line: "m", aIndex: -1, bIndex: 9}
16: {line: "e", aIndex: -1, bIndex: 10}
17: {line: "s", aIndex: -1, bIndex: 11}
请注意,PatienceDiff 不报告名字和姓氏的交换,而是提供一个结果,显示从a 中删除了哪些字符,以及向b 添加了哪些字符,最终结果为b.
编辑:添加了名为 patienceDiffPlus 的新算法。
在仔细考虑了上面提供的最后一个示例之后,该示例显示了 PatienceDiff 在识别可能移动的线条方面的局限性,我突然意识到有一种优雅的方法可以使用 PatienceDiff 算法来确定是否有任何线条确实可能移动了而不是不仅仅是显示删除和添加。
简而言之,我在 PatienceDiff.js 文件的底部添加了 patienceDiffPlus 算法(到上面确定的 GitHub 存储库)。 patienceDiffPlus 算法从初始 patienceDiff 算法中获取删除的 aLines[] 和添加的 bLines[],并再次通过 patienceDiff 算法运行它们。即,patienceDiffPlus 正在寻找可能移动的行的最长公共子序列,因此它将其记录在原始的patienceDiff 结果中。 patienceDiffPlus 算法会继续这个过程,直到找不到更多移动的行。
现在,使用patienceDiffPlus,下面的比较...
let a = "james lebron";
let b = "lebron james";
let difference = patienceDiffPlus( a.split(""), b.split("") );
...返回 difference.lines 包含...
difference.lines: Array(18)
0: {line: "j", aIndex: 0, bIndex: -1, moved: true}
1: {line: "a", aIndex: 1, bIndex: -1, moved: true}
2: {line: "m", aIndex: 2, bIndex: -1, moved: true}
3: {line: "e", aIndex: 3, bIndex: -1, moved: true}
4: {line: "s", aIndex: 4, bIndex: -1, moved: true}
5: {line: " ", aIndex: 5, bIndex: -1, moved: true}
6: {line: "l", aIndex: 6, bIndex: 0}
7: {line: "e", aIndex: 7, bIndex: 1}
8: {line: "b", aIndex: 8, bIndex: 2}
9: {line: "r", aIndex: 9, bIndex: 3}
10: {line: "o", aIndex: 10, bIndex: 4}
11: {line: "n", aIndex: 11, bIndex: 5}
12: {line: " ", aIndex: 5, bIndex: 6, moved: true}
13: {line: "j", aIndex: 0, bIndex: 7, moved: true}
14: {line: "a", aIndex: 1, bIndex: 8, moved: true}
15: {line: "m", aIndex: 2, bIndex: 9, moved: true}
16: {line: "e", aIndex: 3, bIndex: 10, moved: true}
17: {line: "s", aIndex: 4, bIndex: 11, moved: true}
注意添加了moved 属性,该属性标识一行(或本例中的字符)是否可能被移动。同样,patienceDiffPlus 只是匹配已删除的 aLines[] 并添加了 bLines[],因此无法保证这些行确实被移动了,但很有可能它们确实被移动了。