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

前言

本文是关于文本对比和通过diff算法的简单介绍,如果你想快速通过java+html或者纯JavaScript来实现两文件并排对比可看我另一篇文章:

文本对比,文本差异并排对比显示实现

一、文本差异对比介绍

在这里插入图片描述

举一个最常见的例子,我们使用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 差分算法

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

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

常见的文本差异对比库:

1.java-diff-utils

2.diff-match-patch

本文通过java-diff-utils库来实现文本差异对比

二、依赖下载

jar下载网址:io.github.java-diff-utils:java-diff-utils:4.11

官方git地址:java-diff-utils

官方demo示例: java-diff-utils Examples

如果你使用Maven:

<dependency>
  <groupId>io.github.java-diff-utils</groupId>
  <artifactId>java-diff-utils</artifactId>
  <version>4.11</version>
</dependency>

如果你使用Gradle:

implementation 'io.github.java-diff-utils:java-diff-utils:4.11'

本文下面会用到的三个对比文本如下:

test1.txt 和 test3.txt :

_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:

_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

		//原始文件
        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);
        }

输出:

[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

		//原始文件
        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:

--- 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];

说明:

关于输出的diff可参考 :读懂diff

减号代表原始文件test1.txt,加号代表对比文件test2.txt

@@ -10,0 +10,2 @@
+insert1;
+insert2

表示test2增加了2行,分别加了insert1;和insert2这两行

@@ -19,2 +21,3 @@
-window.upList = [];
-window.downList = [];
+
+
+add

表示test2删除了2行,删除的这两行是test1从19行开始的2行;然后test2又增加了3行,增加是3行是从test2的21行开始的。

@@ -27,1 +30,1 @@
- 	location.href = urlsplit[0];
+ 	location.href = qazwer[0];

表示test2删除了1行,删除的这行是test1从27行开始的1行;然后test2又增加了1行,增加是1行是从test2的30行开始的。(相当于这一行进行了修改)

前端页面美化输出:
如果想将上面通过Java代码得到的两个文件的差异美化输出可以 参考我另一篇博客:Java+html实现文本对比 实现效果如下:

在这里插入图片描述
如果想获取全部内容对比,要把diff加入到文件内容中,也就是上面代码的diffString 替换为如下(没改动的代码前要加一个空格才会显示):

    const diffString = `
--- test1.txt
+++ test2.txt
@@ -0,0 +0,0 @@
 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;
@@ -10,0 +10,2 @@			
+insert1;
+insert2;
            break;
        }
    }
    return  flag;
}
 window._assignInfo = {};
 window._curnodepersons = [];
 window.attachmode = '0';
 window.isEsignature = false;
@@ -19,2 +21,3 @@
-window.upList = [];
-window.downList = [];
+
+
+add
 window.billId = ;
 window.upProcessIdList = [];
 window.downProcessIdList = [];
 var urlsplit = window.location.href.split();
 if(urlsplit.length>1){
  //alert(window.location.href);
@@ -27,1 +30,1 @@
- 	location.href = urlsplit[0];
+ 	location.href = qazwer[0];
 }
`;

在这里插入图片描述

如果你想通过js获取diff,然后通过diff2html渲染到界面可以参考:
实现Diff页面的工程实践
vue-diff-demo

五、根据unifiedDiff打补丁

假设你有test1.txt、test2.txt、test3.txt 三个内容一样的文件,有一天你改了test2.txt的文件内容,你想把test2.txt修改的地方也同步的修改运用到 test3.txt上就可以通过打补丁的方式来实现。简单的来说就是test1.txt、test2.txt进行对比得到 unifiedDiff ,把 unifiedDiff运用到 test3.txt 。(相当于git的不同分支的代码同步)

		//原始文件
        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);

        //从文件或此处从内存导入统一差异格式到补丁
        Patch<String> importedPatch = UnifiedDiffUtils.parseUnifiedDiff(unifiedDiff);

        List<String> test3 = Files.readAllLines(new File("D:\\test3.txt").toPath());

        //将差异运用到其他文件打补丁,即将不同点运用到其他文件(相当于git的冲突合并)
        List<String> patchedText = DiffUtils.patch(test3, importedPatch);
        for (String patchedTextPow : patchedText) {
            System.out.println(patchedTextPow);
        }

输出:

_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];
}

六、对比两文件的不同点并按行显示不同

		//原始文件
        List<String> text1= Files.readAllLines(new File("D:\\test2.txt").toPath());
        //对比文件
        List<String> text2=Files.readAllLines(new File("D:\\test1.txt").toPath());

        //行比较器,原文件删除的内容用"~"包裹,对比文件新增的内容用"**"包裹
        DiffRowGenerator generator = DiffRowGenerator.create()
                .showInlineDiffs(true)
                .inlineDiffByWord(true)
                .oldTag(f -> "~")
                .newTag(f -> "**")
                .build();
        //通过行比较器对比得到每一行的不同
        List<DiffRow> rows = generator.generateDiffRows(text1, text2);

        //输出每一行的原始文件和对比文件,每一行的原始文件和对比文件通过 "|"分割
        for (DiffRow row : rows) {
            System.out.println(row.getOldLine() + "|" + row.getNewLine());
        }

输出:

_this.ispc = function(){|_this.ispc = function(){
  var userAgentInfo = navigator.userAgent;|  var userAgentInfo = navigator.userAgent;
    var Agents = ["Android", "iPhone",|    var Agents = ["Android", "iPhone",
                "SymbianOS", "Windows Phone",|                "SymbianOS", "Windows Phone",
                "iPad", "iPod"];|                "iPad", "iPod"];
    var flag = true;|    var flag = true;
    for (var v = 0; v &lt; Agents.length; v++) {|    for (var v = 0; v &lt; Agents.length; v++) {
        if (userAgentInfo.indexOf(Agents[v]) &gt; 0) {|        if (userAgentInfo.indexOf(Agents[v]) &gt; 0) {
            flag = false;|            flag = false;
~insert1;~|
~insert2;~|
            break;|            break;
        }|        }
    }|    }
    return  flag;|    return  flag;
}|}
window._assignInfo = {};|window._assignInfo = {};
window._curnodepersons = [];|window._curnodepersons = [];
window.attachmode = '0';|window.attachmode = '0';
window.isEsignature = false;|window.isEsignature = false;
|**window.upList = [];**
|**window.downList = [];**
~add~|
window.billId = '';|window.billId = '';
window.upProcessIdList = [];|window.upProcessIdList = [];
window.downProcessIdList = [];|window.downProcessIdList = [];
var urlsplit = window.location.href.split("#");|var urlsplit = window.location.href.split("#");
if(urlsplit.length&gt;1){|if(urlsplit.length&gt;1){
  //alert(window.location.href);|  //alert(window.location.href);
     location.href = ~qazwer~[0];|     location.href = **urlsplit**[0];
}|}

参考:

Diffing more quickly
Compare files side by side and hightlight diff using Java | Apache Commons Text diff | Myers algorithm
Myers diff algorithm vs Hunt–McIlroy algorithm

Java-diff-utils是一个用Java编写的开源库,可用于比较文本文件、源代码、XML和HTML文档等。 而Katalon Studio是一个功能强大的自动化测试工具,它可以帮助我们快速准确地完成各种类型的自动化测试。 要实现自动化文件比较,我们可以结合使用这两个工具。以下是一些基本步骤: 1.将Java-diff-utils库添加到Katalon项目中。此步骤可以通过在Katalon中创建新的Java类来实现。然后,将Java-diff-utils的jar文件添加到该类的Java Build Path中。 2.编写脚本来读取需要比较的文件,并将它们传递给Java-diff-utils库进行比较。这样可以获取文件之间的差异列表。 3.将差异列表输出到控制台或存储在文件中,以便后续分析。 4.根据文件差异列表的结果,编写测试用例来验证文件是否已正确更新。 下面是一个简单的示例脚本,用于比较两个文本文件: ``` import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.List; import org.junit.Test; import difflib.DiffUtils; import difflib.Patch; public class FileCompare { @Test public void compareFiles() throws IOException { File file1 = new File("file1.txt"); File file2 = new File("file2.txt"); List<String> lines1 = Files.readAllLines(file1.toPath()); List<String> lines2 = Files.readAllLines(file2.toPath()); Patch patch = DiffUtils.diff(lines1, lines2); List<String> differences = DiffUtils.generateUnifiedDiff("file1", "file2", lines1, patch, 0); for(String line : differences) { System.out.println(line); } } } ``` 此脚本将比较两个文本文件file1.txt和file2.txt,将差异列表输出到控制台。您可以根据需要修改此脚本以执行更复杂的比较操作,例如比较XML或HTML文件。 在Katalon Studio中,您可以将此脚本作为测试用例运行,并编写其他测试用例来验证文件是否已正确更新。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值