springboot 文件差异化对比以及可视化展示

maven依赖

   <!--   文件内容对比--><dependency><groupId>io.github.java-diff-utils</groupId><artifactId>java-diff-utils</artifactId><version>4.11</version></dependency>

创建Diff 工具类

package com.system.utlis.diff;import com.github.difflib.UnifiedDiffUtils;
import com.github.difflib.patch.Patch;
import org.springframework.stereotype.Component;import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;@Component
public class DiffHandleUtils {public static void main(String[] args) {//对比 F:\n1.txt和 F:\n2.txt 两个文件,获得不同点List<String> diffString = DiffHandleUtils.diffString("E:\\test\\test1.java", "E:\\test\\test2.txt");//在F盘生成一个diff.html文件,打开便可看到两个文件的对比DiffHandleUtils.generateDiffHtml(diffString, "E:\\test");System.out.println("diff完成");}/*** 对比两文件的差异,返回原始文件+diff格式** @param original 原文件内容* @param revised  对比文件内容*/public static List<String> diffString(List<String> original, List<String> revised) {return diffString(original, revised, null, null);}/*** 对比两文件的差异,返回原始文件+diff格式** @param original         原文件内容* @param revised          对比文件内容* @param originalFileName 原始文件名* @param revisedFileName  对比文件名*/public static List<String> diffString(List<String> original, List<String> revised, String originalFileName, String revisedFileName) {originalFileName = originalFileName == null ? "原始文件" : originalFileName;revisedFileName = revisedFileName == null ? "对比文件" : revisedFileName;//两文件的不同点Patch<String> patch = com.github.difflib.DiffUtils.diff(original, revised);//生成统一的差异格式List<String> unifiedDiff = UnifiedDiffUtils.generateUnifiedDiff(originalFileName, revisedFileName, original, patch, 0);int diffCount = unifiedDiff.size();if (unifiedDiff.size() == 0) {//如果两文件没差异则插入如下unifiedDiff.add("--- " + originalFileName);unifiedDiff.add("+++ " + revisedFileName);unifiedDiff.add("@@ -0,0 +0,0 @@");} else if (unifiedDiff.size() >= 3 && !unifiedDiff.get(2).contains("@@ -1,")) {unifiedDiff.set(1, unifiedDiff.get(1));//如果第一行没变化则插入@@ -0,0 +0,0 @@unifiedDiff.add(2, "@@ -0,0 +0,0 @@");}//原始文件中每行前加空格List<String> original1 = original.stream().map(v -> " " + v).collect(Collectors.toList());//差异格式插入到原始文件中return insertOrig(original1, unifiedDiff);}/*** 对比两文件的差异,返回原始文件+diff格式** @param filePathOriginal 原文件路径* @param filePathRevised  对比文件路径*/public static List<String> diffString(String filePathOriginal, String filePathRevised) {//原始文件List<String> original = null;//对比文件List<String> revised = null;File originalFile = new File(filePathOriginal);File revisedFile = new File(filePathRevised);try {original = Files.readAllLines(originalFile.toPath());revised = Files.readAllLines(revisedFile.toPath());} catch (IOException e) {e.printStackTrace();}return diffString(original, revised, originalFile.getName(), revisedFile.getName());}/*** 通过两文件的差异diff生成 html文件,打开此 html文件便可看到文件对比的明细内容** @param diffString 调用上面 diffString方法获取到的对比结果* @param htmlPath   生成的html路径,如:/user/var/mbos/ent/21231/*/public static void generateDiffHtml(List<String> diffString, String htmlPath) {StringBuilder builder = new StringBuilder();for (String line : diffString) {builder.append(escapeStr(line));builder.append("\n");}//githubCss来自 https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.1/styles/github.min.cssString githubCss = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.1/styles/github.min.css";//diff2htmlCss来自 https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.cssString diff2htmlCss = "https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css";//diff2htmlJs来自 https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.jsString diff2htmlJs = "https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js";String template = "<!DOCTYPE html>\n" +"<html lang=\"en-us\">\n" +"  <head>\n" +"    <meta charset=\"utf-8\" />\n" +"    <link rel=\"stylesheet\" href=\"" + githubCss + "\" />\n" +"     <link rel=\"stylesheet\" type=\"text/css\" href=\"" + diff2htmlCss + "\" />\n" +"    <script type=\"text/javascript\" src=\"" + diff2htmlJs + "\"></script>\n" +"  </head>\n" +"  <script>\n" +"    const diffString = `\n" +"temp\n" +"`;\n" +"\n" +"\n" +"     document.addEventListener('DOMContentLoaded', function () {\n" +"      var targetElement = document.getElementById('myDiffElement');\n" +"      var configuration = {\n" +"        drawFileList: true,\n" +"        fileListToggle: true,\n" +"        fileListStartVisible: true,\n" +"        fileContentToggle: true,\n" +"        matching: 'lines',\n" +"        outputFormat: 'side-by-side',\n" +"        synchronisedScroll: true,\n" +"        highlight: true,\n" +"        renderNothingWhenEmpty: true,\n" +"      };\n" +"      var diff2htmlUi = new Diff2HtmlUI(targetElement, diffString, configuration);\n" +"      diff2htmlUi.draw();\n" +"      diff2htmlUi.highlightCode();\n" +"    });\n" +"  </script>\n" +"  <body>\n" +"    <div id=\"myDiffElement\"></div>\n" +"  </body>\n" +"</html>";template = template.replace("temp", builder.toString());File f = null; //文件读取为字符流//如果htmlPath文件夹不存在则创建File folder = new File(htmlPath);if (!folder.exists() && !folder.isDirectory()) {folder.mkdirs();}//写入diff.htmltry {f = new File(htmlPath + "diff.html");BufferedWriter buf = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), StandardCharsets.UTF_8)); //文件加入缓冲区buf.write(template); //向缓冲区写入buf.close(); //关闭缓冲区并将信息写入文件} catch (IOException e) {e.printStackTrace();}}//对字符串中的特殊字符进行转义private static String escapeStr(String linStr) {//如果含有反斜杠对其转义为\\if (linStr.contains("\\")) {linStr = linStr.replaceAll("\\\\", "\\\\\\\\");}//如果含有</script>将其转义为<\/script> ,否则浏览器解析到</script>将报错if (linStr.contains("</script>")) {linStr = linStr.replaceAll("</script>", "<\\\\/script>");}//如果含有字符串模板的反引号对其转义为\`if (linStr.contains("`")) {linStr = linStr.replaceAll("`", "\\\\`");}//如果含有$符号对其转义为\$if (linStr.contains("$")) {linStr = linStr.replaceAll("\\$", "\\\\\\$");}return linStr;}//统一差异格式插入到原始文件public static List<String> insertOrig(List<String> original, List<String> unifiedDiff) {List<String> result = new ArrayList<>();//unifiedDiff中根据@@分割成不同行,然后加入到diffList中List<List<String>> diffList = new ArrayList<>();List<String> d = new ArrayList<>();for (int i = 0; i < unifiedDiff.size(); i++) {String u = unifiedDiff.get(i);if (u.startsWith("@@") && !"@@ -0,0 +0,0 @@".equals(u) && !u.contains("@@ -1,")) {List<String> twoList = new ArrayList<>();twoList.addAll(d);diffList.add(twoList);d.clear();d.add(u);continue;}if (i == unifiedDiff.size() - 1) {d.add(u);List<String> twoList = new ArrayList<>();twoList.addAll(d);diffList.add(twoList);d.clear();break;}d.add(u);}//将diffList和原始文件original插入到result,返回resultfor (int i = 0; i < diffList.size(); i++) {List<String> diff = diffList.get(i);List<String> nexDiff = i == diffList.size() - 1 ? null : diffList.get(i + 1);//含有@@的一行String simb = i == 0 ? diff.get(2) : diff.get(0);String nexSimb = nexDiff == null ? null : nexDiff.get(0);//插入到resultinsert(result, diff);//解析含有@@的行,得到原文件从第几行开始改变,改变了多少(即增加和减少的行)Map<String, Integer> map = getRowMap(simb);if (null != nexSimb) {Map<String, Integer> nexMap = getRowMap(nexSimb);int start = 0;if (map.get("orgRow") != 0) {start = map.get("orgRow") + map.get("orgDel") - 1;}int end = nexMap.get("revRow") - 2;//插入不变的insert(result, getOrigList(original, start, end));}int start = (map.get("orgRow") + map.get("orgDel") - 1);start = start == -1 ? 0 : start;if (simb.contains("@@ -1,") && null == nexSimb && map.get("orgDel") != original.size()) {insert(result, getOrigList(original, start, original.size() - 1));} else if (null == nexSimb && (map.get("orgRow") + map.get("orgDel") - 1) < original.size()) {insert(result, getOrigList(original, start, original.size() - 1));}}//如果你想知道两文件有几处不同可以放开下面5行代码注释,会在文件名后显示总的不同点有几处(即预览图中的xxx different),放开注释后有一个小缺点就是如果对比的是java、js等代码文件那代码里的关键字就不会高亮颜色显示,有一点不美观。int diffCount = diffList.size() - 1;if (!"@@ -0,0 +0,0 @@".equals(unifiedDiff.get(2))) {diffCount = diffList.size() > 1 ? diffList.size() : 1;}result.set(1, result.get(1) + " ( " + diffCount + " different )");return result;}//将源文件中没变的内容插入resultpublic static void insert(List<String> result, List<String> noChangeContent) {for (String ins : noChangeContent) {result.add(ins);}}//解析含有@@的行得到修改的行号删除或新增了几行public static Map<String, Integer> getRowMap(String str) {Map<String, Integer> map = new HashMap<>();if (str.startsWith("@@")) {String[] sp = str.split(" ");String org = sp[1];String[] orgSp = org.split(",");//源文件要删除行的行号map.put("orgRow", Integer.valueOf(orgSp[0].substring(1)));//源文件删除的行数map.put("orgDel", Integer.valueOf(orgSp[1]));String[] revSp = org.split(",");//对比文件要增加行的行号map.put("revRow", Integer.valueOf(revSp[0].substring(1)));map.put("revAdd", Integer.valueOf(revSp[1]));}return map;}//从原文件中获取指定的部分行public static List<String> getOrigList(List<String> original1, int start, int end) {List<String> list = new ArrayList<>();if (original1.size() >= 1 && start <= end && end < original1.size()) {for (; start <= end; start++) {list.add(original1.get(start));}}return list;}
}

测试效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/448384.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ copy()函数详细介绍

copy() 是一个标准库函数&#xff0c;位于 头文件中。它用于将一个容器中的元素复制到另一个容器中&#xff0c;或者将一个范围内的元素复制到另一个范围中。 函数参数介绍 copy( first, last, d_first );first 和 last&#xff1a;表示输入范围的迭代器。 first 指向要复制的…

python脚本将照片按时间线整理

说明&#xff1a;有一次自己瞎折腾&#xff0c;然后把服务器相册搞崩了&#xff0c;后来做了备份同步给找了回来&#xff0c;但是相册的时间线全乱了&#xff0c;看起来非常难受。所以就想通过文件夹的形式把照片重新分类&#xff0c;分类后的结构如下(红色字体为文件夹)&#…

openmax

通过EmptyThisBuffer传递未解码的buffer给component&#xff0c;component收到该命令后会去读取input port buffer中的数据&#xff0c;将其组装为帧之后进行解码&#xff0c;buffer处理完成后会通过EmptyBufferDone通知上层输入使用完成&#xff0c;上层收到命令可以继续送输入…

Duplicate entry ‘2020045-2-1‘ for key ‘index_uid‘ 解决方案

项目场景&#xff1a; 今天小编在工作中编写接口对数据库增加相同的非主键数据的时候&#xff0c;突然出现了这样的一个错误&#xff1a; 下面我来给大家解答这个错误的出现原因以及解决办法。 问题描述 Duplicate entry 2020045-2-1 for key index_uid 这个错误大概意思就是…

举个栗子!Tableau 技巧(263):按需突出显示文本表的 N 个行

我们分享过 &#x1f330; &#xff1a;突出显示文本表的行或列&#xff0c;可以突出显示文本表中的某一行或某一列。有数据粉提出新的问题&#xff1a;如果想突出显示多行数据&#xff0c;该如何实现呢&#xff1f; 在 Tableau 中是可以实现的&#xff08;如上图&#xff09;&…

MySQL主从同步

一、配置主服务器 1、更改配置文件 vi /etc/my.cnf [mysqld] server_id100 log-bin/mnt/data/mysql8_data/log-bin 2、重启MySQL service mysql restart 3、创建主从同步用户 CREATE USER rep% IDENTIFIED BY Future2050; GRANT REPLICATION SLAVE ON *.* TO rep%; 4、查询Mas…

Linux下find命令详解

find #查找文件 #按照文件名、大小、时间、权限、类型、所属者、所属组来搜索文件 格式&#xff1a; find 查找路径 查找条件 具体条件&#xff08;按文件名或时间大小等&#xff09; 操作 注意&#xff1a; find命令默认的操作是print输出 find是检索…

2024/2/2学习记录

Mock.js Mock.mock(template) 根据数据模板生成模拟数据 Mock.mock(rurl,template) 记录数据模板&#xff0c;当拦截到 rurl 的 ajax 请求时&#xff0c;将根据数据模板 template 生成模拟数据&#xff0c;并作为响应数据 返回 Mock.mock(rurl,function(options)) 记…

OpenCV-Python图形图像处理:自用的一些工具函数源代码(统信UOS Linux版)

☞ ░ 前往老猿Python博客 ░ https://blog.csdn.net/LaoYuanPython 一、引言 在《OpenCV-Python图形图像处理:自用的一些工具函数功能及调用语法介绍》介绍了笔者自用的一些函数的语法及功能,在《OpenCV-Python图形图像处理:自用的一些工具函数源代码》介绍了老猿实现的代…

代码随想录算法训练营DAY10 | 栈与队列 (1)

理论基础及Java实现参考文章&#xff1a;栈和队列 一、LeetCode 232 用栈实现队列 题目链接&#xff1a;232.用栈实现队列https://leetcode.cn/problems/implement-queue-using-stacks/ 思路&#xff1a;使用两个栈stack1、stack2实现队列&#xff1b;stack1用来存储入队元素&…

元素的显示与隐藏,精灵图,字体图标,CSSC三角

元素的显示与隐藏 类似网站广告&#xff0c;当我们点击关闭就不见了&#xff0c;但是我们重新刷新页面&#xff0c;会重新出现 本质&#xff1a;让元素在页面中隐藏或者显示出来。 1.display显示隐藏 2.visibility显示隐藏 3.overflow溢出显示隐藏 1.display属性&#xff08;…

QT研究笔记(二)Qt 5.14.2 简单使用-使用vs2022创建第一个QT项目

前一篇文章&#xff0c;我们介绍了什么是Qt &#xff1f;为什么要使用Qt&#xff1f;以及Qt在windows 环境下的安装和部署。喜欢的小伙伴&#xff0c;可以前往查看。 QT研究笔记&#xff08;一&#xff09;windows 开发环境安装部署。 从这篇文章开始&#xff0c;我们正式开始…