比对word文档并提取差异片段(java版)

整体比较

有时候,我们想比对两个word文档,标记出两个文档之间的差异,这样一眼就能看出来修改了哪些地方,如下图,左边文档中的扩招2000人删除了,辞呈改成了说明,新增了并且加重处罚等文字,是否一目了然了。

代码实现:

package cn.hollycloud;

import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.commons.text.diff.CommandVisitor;
import org.apache.commons.text.diff.EditScript;
import org.apache.commons.text.diff.StringsComparator;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.junit.jupiter.api.Test;

import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;

public class DocTest {
    @Test
    public void testCompare() {
        try {
            // 读取word文档
            XWPFDocument doc1 = new XWPFDocument(new FileInputStream("D:\\doc\\1.docx"));
            XWPFDocument doc2 = new XWPFDocument(new FileInputStream("D:\\doc\\2.docx"));
            // 获取文档文本内容
            XWPFWordExtractor extractor1 = new XWPFWordExtractor(doc1);
            String content1 = extractor1.getText();
            XWPFWordExtractor extractor2 = new XWPFWordExtractor(doc2);
            String content2 = extractor2.getText();
            // 关闭流
            doc1.close();
            doc2.close();
            // commons-text api有很大调整,请注意你使用的版本,我使用的版本为1.11.0
            StringsComparator comparator = new StringsComparator(content1, content2);
            EditScript<Character> script = comparator.getScript();
            SectionDiffCommandVisitor commandVisitor = new SectionDiffCommandVisitor();
            script.visit(commandVisitor);
            commandVisitor.finish();
            System.out.println(commandVisitor.getLeftTemp());
            System.out.println(commandVisitor.getRightTemp());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 源文本上显示变化内容
    static class SectionDiffCommandVisitor implements CommandVisitor<Character> {
        private List<String> leftContents = new ArrayList<>();
        private List<String> rightContents = new ArrayList<>();
        private StringBuilder leftTemp = new StringBuilder();
        private StringBuilder rightTemp = new StringBuilder();
        private int lastTag = 0; //0:keep,1:insert,2:delete
        private String insertStart = "<em>";
        private String insertEnd = "</em>";
        private String deleteStart = "<del>";
        private String deleteEnd = "</del>";

        @Override
        public void visitDeleteCommand(Character object) {
            if (lastTag == 1) {
                rightTemp.append(insertEnd);
                leftTemp.append(deleteStart);
            } else if (lastTag == 0) {
                leftTemp.append(deleteStart);
            }
            leftTemp.append(object);
            lastTag = 2;
        }

        @Override
        public void visitInsertCommand(Character object) {
            if (lastTag == 2) {
                leftTemp.append(deleteEnd);
                rightTemp.append(insertStart);
            } else if (lastTag == 0) {
                rightTemp.append(insertStart);
            }
            rightTemp.append(object);
            lastTag = 1;
        }

        @Override
        public void visitKeepCommand(Character object) {
            finish(object);
        }

        public void finish() {
            finish(null);
        }

        private void finish(Object object) {
            if (lastTag == 1) {
                rightTemp.append(insertEnd);
            } else if (lastTag == 2) {
                leftTemp.append(deleteEnd);
            }
            if (object != null) {
                leftTemp.append(object);
                rightTemp.append(object);
            }
            lastTag = 0;
        }

        public StringBuilder getLeftTemp() {
            return leftTemp;
        }

        public StringBuilder getRightTemp() {
            return rightTemp;
        }
    }
}

列出变化的片段

如果一个文档很长,这样比对效率就比较低了,我们只需要看有差异的片段,下面我用《赡养人类》这部小说尝试,修改了里面部分内容,列出了修改的片段,效果还不错

代码:

package cn.hollycloud;

import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.commons.text.diff.CommandVisitor;
import org.apache.commons.text.diff.EditScript;
import org.apache.commons.text.diff.StringsComparator;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.junit.jupiter.api.Test;

import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class DocTest {
    @Test
    public void testCompare() {
        try {
            // 读取word文档
            XWPFDocument doc1 = new XWPFDocument(new FileInputStream("D:\\doc\\1.docx"));
            XWPFDocument doc2 = new XWPFDocument(new FileInputStream("D:\\doc\\2.docx"));
            // 获取文档文本内容
            XWPFWordExtractor extractor1 = new XWPFWordExtractor(doc1);
            String content1 = extractor1.getText();
            XWPFWordExtractor extractor2 = new XWPFWordExtractor(doc2);
            String content2 = extractor2.getText();
            // 关闭流
            doc1.close();
            doc2.close();
            // commons-text api有很大调整,请注意你使用的版本,我使用的版本为1.11.0
            StringsComparator comparator = new StringsComparator(content1, content2);
            EditScript<Character> script = comparator.getScript();
            SectionDiffCommandVisitor commandVisitor = new SectionDiffCommandVisitor();
            script.visit(commandVisitor);
            commandVisitor.finish();
            List<DiffSection> diffSections = commandVisitor.getDiffSections();
            for (int i = 0; i < diffSections.size(); i++) {
                System.out.println("差分内容#" + (i + 1) + ": ");
                System.out.println(diffSections.get(i).getLeftContent());
                System.out.println(diffSections.get(i).getRightContent());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Data
    public static class DiffSection {
        private String leftContent;
        private String rightContent;
    }

    // 源文本上显示变化内容
    static class SectionDiffCommandVisitor implements CommandVisitor<Character> {
        private List<DiffSection> diffSections = new ArrayList<>();
        private StringBuilder leftTemp = new StringBuilder();
        private StringBuilder rightTemp = new StringBuilder();
        private int lastTag = 0; //0:keep,1:insert,2:delete
        private String insertStart = "<em>";
        private String insertEnd = "</em>";
        private String deleteStart = "<del>";
        private String deleteEnd = "</del>";
        private Pattern pattern = Pattern.compile("[。\\r?\\n\\.]");
        private boolean hasChange = false;

        @Override
        public void visitDeleteCommand(Character object) {
            if (lastTag == 1) {
                rightTemp.append(insertEnd);
                leftTemp.append(deleteStart);
            } else if (lastTag == 0) {
                leftTemp.append(deleteStart);
            }
            leftTemp.append(object);
            lastTag = 2;
            hasChange = true;
        }

        @Override
        public void visitInsertCommand(Character object) {
            if (lastTag == 2) {
                leftTemp.append(deleteEnd);
                rightTemp.append(insertStart);
            } else if (lastTag == 0) {
                rightTemp.append(insertStart);
            }
            rightTemp.append(object);
            lastTag = 1;
            hasChange = true;
        }

        @Override
        public void visitKeepCommand(Character object) {
            finish(object);
        }

        public void finish() {
            finish(null);
        }

        private void finish(Object object) {
            if (lastTag == 1) {
                rightTemp.append(insertEnd);
            } else if (lastTag == 2) {
                leftTemp.append(deleteEnd);
            }
            if (object != null) {
                leftTemp.append(object);
                rightTemp.append(object);
            }
            // 到句子末尾
            if (object == null || pattern.matcher(object.toString()).find()) {
                if (hasChange && (isLegalStr(leftTemp.toString()) || isLegalStr(rightTemp.toString()))) {
                    DiffSection diffSection = new DiffSection();
                    diffSection.setLeftContent(leftTemp.toString());
                    diffSection.setRightContent(rightTemp.toString());
                    diffSections.add(diffSection);
                }
                leftTemp.setLength(0);
                rightTemp.setLength(0);
                hasChange = false;
            }
            lastTag = 0;
        }

        private boolean isLegalStr(String str) {
            if (StringUtils.isEmpty(str)) return false;
            String cleaned = str.replaceAll("[\\p{P}\\s]+", "")
                    .replace(insertStart, "").replace(insertEnd, "")
                    .replace(deleteStart, "").replace(deleteEnd, "");
            return !StringUtils.isEmpty(cleaned);
        }

        public List<DiffSection> getDiffSections() {
            return diffSections;
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DP背包模型

目录 采药&#xff08;01背包&#xff09;代码实现 装箱问题&#xff08;01背包&#xff09;代码实现 *宠物小精灵之收服&#xff08;二维费用01背包&#xff09;题目分析代码实现 数字组合&#xff08;01背包&#xff09;代码实现 买书&#xff08;完全背包&#xff09;代码实…

【学习】软件测试中误区汇总分析

大家有没有想过这个问题&#xff1a;软件测试中有哪些误区呢&#xff1f;想起这个题目&#xff0c;是因为最近遇到好几次关于这方面的讨论。发觉即便做过几年测试的老员工也或多或少有些这方面的困惑。当然一家之言&#xff0c;仅作抛砖引玉之谈。 误区一&#xff1a;测试就是…

git提交-分支开发合并-控制台操作

git提交-分支开发合并-控制台操作 git的基本概念工作区、暂存区和版本库工作区&#xff1a;就是你在电脑里能看到的目录&#xff08;隐藏目录 .git不算工作区&#xff09;。暂存区&#xff1a;英文叫 stage 或 index。一般存放在本地的.git目录下的index 文件&#xff08;.git/…

【机器学习之---数学】统计学基础概念

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 统计学基础 1. 频率派 频率学派&#xff08;传统学派&#xff09;认为样本信息来自总体&#xff0c;通过对样本信息的研究可以合理地推断和估计总体信息…

为什么requests不是python标准库?

在知乎上看到有人问&#xff1a;为什么requests不是python标准库&#xff1f; 这确实是部分人困惑的问题&#xff0c;requests作为python最受欢迎的http请求库&#xff0c;已经成为爬虫必备利器&#xff0c;为什么不把requests直接装到python标准库里呢&#xff1f;可以省去第…

rostopic echo /tf 筛选特定数据

rostopic echo /tf 筛选特定数据 在使用rostopic echo命令时&#xff0c;您可以使用参数-n指定输出的消息数量&#xff0c;并且可以使用参数-p将输出以消息格式打印。然而&#xff0c;rostopic echo命令本身并不支持直接筛选指定的消息。 如果想要筛选特定的消息&#xff0c;…

【Java程序设计】【C00368】基于(JavaWeb)Springboot的箱包存储系统(有论文)

TOC 博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;博客中有上百套程序可供参考&#xff0c;欢迎共同交流学习。 项目简介 项目获取 &#x1f345;文末点击卡片…

Mybatis-核心配置文件 / Mybatis增删改查

1. 核心配置文件 1.1. 概述 核心配置文件是MyBatis框架中用于集中定义全局配置信息的XML文件&#xff0c;其内部包含了一系列预设标签&#xff0c;用于设置数据库连接、对象映射、类型处理等关键参数。这些标签遵循特定的排列顺序&#xff0c;尽管并非所有标签都是强制性的&a…

【LVGL-选项卡部件(lv_tabview_create)】

LVGL-选项卡部件&#xff08;lv_tabview_create&#xff09; ■ LVGL-选项卡部件&#xff08;lv_tabview_create&#xff09;■ 综合示例&#xff1a; ■ LVGL-选项卡部件&#xff08;lv_tabview_create&#xff09; ■ 综合示例&#xff1a; 装饰部分

06_Request

文章目录 前置知识点URL和URIHTTP请求报文和HTTP响应报文 Request请求行请求头请求体特殊信息获取客户机和服务器主机信息 请求参数直接封装引用类型 POST请求请求参数乱码文件上传案例&#xff08;与前面的getServletContext结合&#xff09; Request做请求的转发 前置知识点 …

pip安装pyqt5报错

已解决pip安装pyqt5报错 ERROR: Could not build wheels for PyQt5-sip, which is required to install pyproject.toml-based projects 安装C生成工具

查询 in条件下按顺序排序

查询语句 select * from user where id in (5,21,6);查询结果是不是按照参数顺序排列的&#xff0c;为了保证查询顺序可以使用 select * from sj_user where id in(5,21,6) order by FIELD(id,5,21,6); //或者 select * from sj_user where id in(5,21,6) order by FIND_IN_S…

MFC标签设计工具 图片控件上,移动鼠标显示图片控件内的鼠标xy的水平和垂直辅助线要在标签模板上加上文字、条型码、二维码 找准坐标和字体大小 源码

需求&#xff1a;要在标签模板上加上文字、条型码、二维码 找准坐标和字体大小 我生成标签时&#xff0c;需要对齐和 调文字字体大小。这工具微调 能快速知道位置 和字体大小。 标签设计(点击图片&#xff0c;上下左右箭头移动 或-调字体) 已经够用了&#xff0c;滚动条还没完…

使用docker-compose搭建wordpress博客

1、从远程仓库拉取worldpress镜像到本地 2、新建一个项目&#xff0c;然后在新建的项目目录里面新建一个docker-compose.yml模版文件。 3、编写docker-compose.yml文件 4、docker-compose up 运行项目。 5、在浏览器测试 使用docker-compose搭建wordpress博客实验成功。

过滤器 Filter

目录 1、Filter是什么 2、原理 3、怎样使用 步骤&#xff1a; Filter的执行流程&#xff1a; 拦截路径配置&#xff1a; 配置方式&#xff1a; 过滤器链&#xff1a; 1、Filter是什么 Filter是一个在计算机中用于筛选、过滤和修改数据的组件或模块。它在数据传输和处理…

python入门题:输入输出练习

以下是Python基础语法的练习&#xff0c;项目要求和代码如下&#xff1a; """ 例3&#xff1a;小精灵&#xff1a;你好&#xff0c;欢迎古灵阁&#xff0c;请问您需要帮助吗&#xff1f;需要or不需要&#xff1f; 你&#xff1a;需要 小精灵&#xff1a;请问你需…

python 中判断文件、目录是否存在的方法

判断目录是否存在并创建目录 一、实现上传文件功能二、判断目录是否存在的办法2.1、使用os模块2.1.1、判断目录是否存在2.1.2、os.makedirs()&#xff1a;递归创建目录 2.2、使用pathlib模块2.2.1、path.exist()判断目录是否存在2.2.1、path.mkdir()&#xff1a;创建目录 2.3、…

深入聊聊企业数字化转型这个事儿

01 什么是数字化&#xff1f; 聊数字化&#xff0c;就不得不聊聊信息化、智能化。佛性的说&#xff1a;信息化是数字化的前世&#xff0c;智能化是数字化的来生&#xff01;我习惯用一个结构化的图形来表示事物之间的关系&#xff0c;信息化、数字化、智能化的关系如下&#…

后端基础篇- 社区 IDEA 手动 Maven 创建 SpringBoot 项目、Maven 安装与配置环境变量、IDEA 集成 Maven

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 Maven 安装与配置环境变量 1.1 下载并解压安装包 1.2 配置本地仓库 1.3 配置阿里云私服 1.4 配置环境变量 2.0 IDEA 集成 Maven 2.1 首先创建一个新项目 2.2 开始…

Unity编辑器功能 将选中的文件夹复制一份到其他文件夹

[MenuItem("Ab包工具/将选中的文件移动到StreamingAssets文件夹下")] public static void MoveFireToStreamA() { //得到选中文件的数组 Object[] selectobj Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets); i…