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

news2024/11/14 13:52:06

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.css
    String 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.css
    String diff2htmlCss = "https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css";
    //diff2htmlJs来自 https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js
    String 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.html
    try {
      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,返回result
    for (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);
      //插入到result
      insert(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;
  }

  //将源文件中没变的内容插入result
  public 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.coloradmin.cn/o/1424354.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大路灯和台灯哪个更适合学生?五款适合学生的大路灯分享

根据国家卫健委在上个月公布的数据&#xff0c;我国儿童青少年总体近视率为52.7%&#xff0c;其中&#xff0c;小学生为35.6%&#xff0c;初中生为71.1%&#xff0c;高中生为80.5%。随着这个数据公布以来&#xff0c;许多家长开始紧张自家孩子的近视问题&#xff0c;不少家长为…

Vue学习Element-ui

声明&#xff1a;本文来源于黑马程序员PDF讲义 Ajax 我们前端页面中的数据&#xff0c;如下图所示的表格中的学生信息&#xff0c;应该来自于后台&#xff0c;那么我们的后台和前端是 互不影响的2个程序&#xff0c;那么我们前端应该如何从后台获取数据呢&#xff1f;因为是2…

【FAS Survey】《Deep learning for face anti-spoofing: A Survey》

PAMI-2022 最新成果&#xff1a;https://github.com/ZitongYu/DeepFAS 文章目录 1 Introduction & Background1.1 Face Spoofing Attacks1.2 Datasets for Face Anti-Spoofing1.3 Evaluation Metrics1.4 Evaluation Protocols 2 Deep FAS with Commercial RGB Camera2.1 H…

幻兽帕鲁服务器游戏怎么升级版本?

幻兽帕鲁服务器游戏怎么升级版本&#xff1f;自建幻兽帕鲁服务器进入Palworld游戏提示“您正尝试加入的比赛正在运行不兼容的游戏版本&#xff0c;请尝试升级游戏版本”什么原因&#xff1f;这是由于你的客户端和幻兽帕鲁服务器版本不匹配&#xff0c;如何解决&#xff1f;更新…

pinctrl/gpio子系统(1)-pinctrl子系统介绍及驱动源码分析

1.简介 在如今的驱动开发工作中&#xff0c;实际上已经很少去对着寄存器手册进行驱动开发了&#xff0c;一般板子拿到手&#xff0c;已经有原厂的驱动开发工程师&#xff0c;在gpio子系统、pinctrl子系统中将自家芯片的引脚适配好了。 我们直接基于设备树已配置好的寄存器值&a…

jsonpath相关---JSONPath - 用于 JSON 的 XPath

一.简介 XML 的一个经常强调的优点是提供了大量工具来分析、转换和有选择地从 XML 文档中提取数据。XPath 就是这些强大的工具之一。 现在是时候想知道&#xff0c;是否需要像 XPath4JSON 这样的东西&#xff0c;以及它可以解决哪些问题。 无需特殊脚本&#xff0c;即可以交…

阿赵UE学习笔记——14、LOD

阿赵UE学习笔记目录   大家好&#xff0c;我是阿赵。   继续学习虚幻引擎的用法。这次看看虚幻引擎的Level Of Detail(LOD)的用法。 一、测试场景准备 用植物系统&#xff0c;在地形上面刷了好多草&#xff1a; 这个时候看一下网格&#xff0c;会发现网格比较多和密集。 …

中国的茶文化:历史、传统与生活

中国的茶文化&#xff1a;历史、传统与生活 一、引言 茶&#xff0c;这一神奇而古老的饮品&#xff0c;与中国的历史、文化和生活方式紧密相连。中国的茶文化&#xff0c;源远流长&#xff0c;博大精深&#xff0c;是中华文明的重要组成部分。它不仅是一种饮料&#xff0c;更是…

阿里云推出 3.x Java 探针,解锁应用观测与治理的全新姿势

作者&#xff1a;张铭辉、泮圣伟 前言 随着春节大促即将到来&#xff0c;为了确保线上业务高效稳定地运行&#xff0c;电商企业大多会对旗下关键业务应用进行多轮测试。通过模拟线上较高流量的请求&#xff0c;来观察服务性能的实际表现。以某企业的业务测试报告举例&#xf…

vue-cli初始化项目很慢?

第一种情况 大部分是由于npm的镜像源不是淘宝的 cmd输入npm config get registry查看是不是淘宝的&#xff0c;是的话看第二种情况试试不是的话输入npm config set registry https://registry.npm.taobao.org 第二种情况 vue-cli配置文件不是使用淘宝镜像源的 找到文件.vue…

sklearn 计算 tfidf 得到每个词分数

from sklearn.feature_extraction.text import TfidfVectorizer# 语料库 可以换为其它同样形式的单词 corpus [list(range(-5, 5)),list(range(-6,4)),list(range(12)),list(range(13))]# corpus [ # [Two, wrongs, don\t, make, a, right, .], # [The, pen, is, might…

深入浅出HBase:一文理解HBase基础概念(列存储、时间戳、key-value)、架构特点以及适合的使用场景

文章目录 一. HBase 数据模型1. 行存储与列式存储1.1. 行存储1.2. 列存储 2. HBase 数据模型2.1. 模型概览2.2. 列与列族2.3. 时间戳&#xff1a;定义数据版本2.4. HBase的Key-Value 三. HBase架构1. HBase读写流程简述2. HRegionServer内部内部数据流转&#xff1a;HRegion &l…

SparkStreaming---入门

文章目录 1.SparkStreaming简介1.1 流处理和批处理1.2 实时和离线1.3 SparkStreaming是什么1.4 SparkStreaming架构图 2.背压机制3.DStream案例实操 1.SparkStreaming简介 1.1 流处理和批处理 流处理和批处理是两种不同的数据处理方式&#xff0c;它们在处理数据的方式和特点…

【GameFramework框架】一、框架介绍

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 【GameFramework框架】系列教程目录&#xff1a; https://blog…

Flink 读取 Kafka 消息写入 Hudi 表无报错但没有写入任何记录的解决方法

博主历时三年精心创作的《大数据平台架构与原型实现:数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行,点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,京东购书链接:https://item.jd.com/12677623.html,扫描左侧二维…

node,node-sass,sass-loader之间的版本关系

前言 安装配置node-sass 以及 sass-loader想必是很多前端的噩梦–一不小心又不成功还得装个半天。 下面说一下这个问题。 当然&#xff0c;你肯定遇到过&#xff1a; Node Sass version 9.0.0 is incompatible with ^4.0.0-这样的问题&#xff0c;这个也是因为三者关系对不上…

【HTML 基础】表单标签

文章目录 1. <form>2. <input>3. <select> 和 <option>4. <textarea>5. <button>结语 HTML 表单是互联网上交互性最强的元素之一&#xff0c;它允许用户输入、选择和提交数据。在这篇博客中&#xff0c;我们将介绍 HTML 中一些关键的表单标…

手把手教你写架构(java)篇

领取资源在文章末尾。 架构部分&#xff1a; 1. 分层架构&#xff08;Layered Architecture&#xff09;&#xff1a;将应用程序分解为多个逻辑层&#xff0c;每个层都有明确的职责。常见的分层包括表示层&#xff08;Presentation Layer&#xff09;、业务逻辑层&#xff08…

torchvision.models._utils.IntermediateLayerGetter()使用

torchvision.models._utils.IntermediateLayerGetter&#xff08;&#xff09;使用 源码如下&#xff1a; from collections import OrderedDictimport torch from torch import nnclass IntermediateLayerGetter(nn.ModuleDict):"""Module wrapper that ret…

电脑配置在哪里看?别错过这四个方法

在使用电脑的日常操作中&#xff0c;了解电脑的硬件和软件配置是解决问题、优化性能以及购买新硬件的关键。然而&#xff0c;对于一些用户来说&#xff0c;查看电脑配置可能是一个看似复杂的任务。幸运的是&#xff0c;有多种简便而直观的方法&#xff0c;让您能够轻松获取电脑…