差异

举一个最常见的例子,我们使用git进行提交时,通常会使用 git diff --cached 来查看这次提交做了哪些改动,这里我们先简单定义一下什么是diff:

diff就是目标文本和源文本之间的区别,也就是将源文本变成目标文本所需要的操作。

差异算法

在 Git 中,有四种diff算法,分别是Myers、Minimal、Patience和Histogram,它们用于获取位于两个不同提交中的两个相同文件的差异。

Myers算法由Eugene W.Myers在1986年发表的一篇论文中提出,是一个能在大部分情况产生”最短的直观的“diff的一个算法。

文本差异对比涉及到的算法介绍:

How different are different diff algorithms in Git

Myers Diff 差分算法

对比工具

好用的在线文本对比: 在线文本比较工具

好用的桌面文本对比软件:

Meld

DiffMerge

xxdiff

Diffuse

Kompare

差异库

  1. java-diff-utils

  2. diff-match-patch

使用例子

maven

  [xml]
1
2
3
4
5
<dependency> <groupId>io.github.java-diff-utils</groupId> <artifactId>java-diff-utils</artifactId> <version>4.11</version> </dependency>

文本

  • test1.txt 和 test3.txt
  [js]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
_this.ispc = function(){ var userAgentInfo = navigator.userAgent; var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; var flag = true; for (var v = 0; v < Agents.length; v++) { if (userAgentInfo.indexOf(Agents[v]) > 0) { flag = false; break; } } return flag; } window._assignInfo = {}; window._curnodepersons = []; window.attachmode = '0'; window.isEsignature = false; window.upList = []; window.downList = []; window.billId = ''; window.upProcessIdList = []; window.downProcessIdList = []; var urlsplit = window.location.href.split("#"); if(urlsplit.length>1){ //alert(window.location.href); location.href = urlsplit[0]; }
  • test2.txt
  [js]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
_this.ispc = function(){ var userAgentInfo = navigator.userAgent; var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; var flag = true; for (var v = 0; v < Agents.length; v++) { if (userAgentInfo.indexOf(Agents[v]) > 0) { flag = false; insert1 insert2 break; } } return flag; } window._assignInfo = {}; window._curnodepersons = []; window.attachmode = '0'; window.isEsignature = false; add window.billId = ''; window.upProcessIdList = []; window.downProcessIdList = []; var urlsplit = window.location.href.split("#"); if(urlsplit.length>1){ //alert(window.location.href); location.href = qazwer[0]; }

获取两文件的不同点-patch

  [java]
1
2
3
4
5
6
7
8
9
10
11
//原始文件 List<String> original = Files.readAllLines(new File("D:\\test1.txt").toPath()); //对比文件 List<String> revised = Files.readAllLines(new File("D:\\test2.txt").toPath()); //两文件的不同点 Patch<String> patch = DiffUtils.diff(original, revised); for (AbstractDelta<String> delta : patch.getDeltas()) { System.out.println(delta); }

输出:

  [plaintext]
1
2
3
[InsertDelta, position: 9, lines: [insert1;, insert2;]] [ChangeDelta, position: 18, lines: [window.upList = [];, window.downList = [];] to [, , add]] [ChangeDelta, position: 26, lines: [ location.href = urlsplit[0];] to [ location.href = qazwer[0];]]

InsertDelta代表插入的,ChangeDelta代表删除的或修改的。position代表第几行,lines代表内容。

根据patch生成统一的差异格式unifiedDiff

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
//原始文件 List<String> original = Files.readAllLines(new File("D:\\test1.txt").toPath()); //对比文件 List<String> revised = Files.readAllLines(new File("D:\\test2.txt").toPath()); //两文件的不同点 Patch<String> patch = DiffUtils.diff(original, revised); //生成统一的差异格式 List<String> unifiedDiff = UnifiedDiffUtils.generateUnifiedDiff("test1.txt", "test2.txt", original, patch, 0); unifiedDiff.forEach(System.out::println);

输出 diff:

  [plaintext]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
--- test1.txt +++ test2.txt @@ -10,0 +10,2 @@ +insert1; +insert2; @@ -19,2 +21,3 @@ -window.upList = []; -window.downList = []; + + +add @@ -27,1 +30,1 @@ - location.href = urlsplit[0]; + location.href = qazwer[0];

前端页面美化输出:

如果想将上面通过Java代码得到的两个文件的差异美化输出可以 参考我另一篇博客:

Java+html实现文本对比 实现效果如下:

小结

多思考。

参考资料

Java 文本内容差异对比实现介绍