当前位置: 首页 > news >正文

Java比较两个Excel是否内容一致

领导每天让比较两个Excel中的内容,为了节省工作效率多摸鱼,就写了个java接口,通过上传两个文件 进行代码比较得到详细的比较结果(这个需要自己根据日志二开) 目前只实现了比较功能

话不多说直接上代码,具体看注释

package com.yxy.aob.util;import cn.hutool.core.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;/*** @Description: excel对比* @Author: Hyz* @Date: 2024/10/14 11:33* @Version:1.0*/
@Slf4j
public class CompareTwoExcels {private final static String MSG_PREFIX = "【source】与【target】中";/*** 文件1流*/private InputStream sourceExcelFile;/*** 文件2流*/private InputStream targetExcelFile;/*** 是否模糊   true为精准匹配  false为模糊匹配*/private boolean isPerfectMatch;/*** 差集*/private List<String> differential;/*** 错误行*/private List<String> errorMsg;/*** 匹配行*/private List<String> successMsg;/*** 构造方法** @param sourceExcelFile 文件1* @param targetExcelFile 文件2* @param isPerfectMatch  表示是否需要完全匹配*/public CompareTwoExcels(InputStream sourceExcelFile, InputStream targetExcelFile, boolean isPerfectMatch) {this.sourceExcelFile = sourceExcelFile;this.targetExcelFile = targetExcelFile;this.isPerfectMatch = isPerfectMatch;this.differential = new ArrayList<>();this.errorMsg = new ArrayList<>();this.successMsg = new ArrayList<>();}public static void main(String[] args) throws IOException {try (InputStream sourceInputStream = Files.newInputStream(Paths.get("C:\\Users\\Administrator\\Desktop\\1.xlsx"));InputStream targetInputStream = Files.newInputStream(Paths.get("C:\\Users\\Administrator\\Desktop\\1 - 副本.xlsx"))) {// 初始化识别器CompareTwoExcels compareTwoExcels = new CompareTwoExcels(sourceInputStream, targetInputStream, false);// 执行识别方法 需要指定对应的sheet页 下标0开始boolean result = compareTwoExcels.comparedExcels(0, 0);log.info("result为:" + result);} catch (Exception e) {e.printStackTrace();}}/*** 获取初始化文件的sheet数量** @param type 1 表示sourceExcelFile 2 表示targetExcelFile*/private int getSheetNum(Integer type) {log.info("sourceExcelSheetNum为:" + this.sourceExcelFile);try (Workbook workbook = WorkbookFactory.create(type == 1 ? this.sourceExcelFile : this.targetExcelFile)) {return workbook.getNumberOfSheets();} catch (IOException e) {return 0;}}/*** 比较两个excel** @param sourceExcelSheetNum sheet  从0开始* @param targetExcelSheetNum sheet  从0开始*/private boolean comparedExcels(int sourceExcelSheetNum, int targetExcelSheetNum) {log.info("------------------------读取Excel1数据------------------------");List<List<String>> sourceList = this.readExcel(sourceExcelSheetNum, this.sourceExcelFile, "source");log.info("------------------------读取Excel2数据------------------------");List<List<String>> targetList = this.readExcel(targetExcelSheetNum, this.targetExcelFile, "target");if (CollUtil.isEmpty(sourceList) || CollUtil.isEmpty(targetList)) {log.error("sourceList为空!!!");return false;}// 行数不一致直接返回falseif (sourceList.size() != targetList.size()) {log.error("sourceList与targetList长度不一致");return false;}log.info("------------------------开始匹配:【{}】------------------------", isPerfectMatch ? "精准匹配" : "模糊匹配");boolean result = true;try {// 模糊匹配和精准匹配区分if (!isPerfectMatch) {// 模糊匹配 即两份excel的每一行不定for (int i = 1; i <= sourceList.size(); i++) {log.info("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 第【{}】行 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓", i);// 表格1的第i行List<String> list1 = sourceList.get(i - 1);List<String> list2 = targetList.get(i - 1);// 判断1、两个表格的同一行是否相同 (即表格1的第1行和表格2的第1行hashCode是否一致)if (list1.hashCode() != list2.hashCode()) {log.info("------{}第【{}】行匹配结果------UNKNOWN", MSG_PREFIX, i);// 判断2、 需要比较表格1的这一行在表格二中是否存在if (targetList.contains(list1)) {int i1 = targetList.indexOf(list1);log.info("---------详情:【source】中第【{}】行与【target】中第【{}】行匹配(跨行)", i, i1 + 1);this.successMsg.add("【source】中第【" + i + "】行与【target】中第【" + (i1 + 1) + "】行匹配(跨行)");//                            List<String> cells = targetList.get(targetList.indexOf(list1));
//                            for (int j = 1; j <= list1.size(); j++) {
//                                if (cells.contains(list1.get(j - 1))) {
//                                    log.info("---------详情:第【{}】行,第【{}】列 匹配", i, j);
//                                    this.successMsg.add(MSG_PREFIX + "第【" + i + "】行,第【" + j + "】列 匹配");
//                                } else {
//                                    log.info("---------详情:第【{}】行,第【{}】列 不匹配", i, j);
//                                    this.errorMsg.add(MSG_PREFIX + "第【" + i + "】行,第【" + j + "】列 不匹配");
//                                    result = false;
//                                }
//                            }} else {log.info("---------详情:【source】中第【{}】行在【target】中不存在", i);this.errorMsg.add(MSG_PREFIX + "第【" + i + "】行均不存在");result = false;}} else {log.info("------{}第【{}】行匹配结果------SUCCESS", MSG_PREFIX, i);this.successMsg.add(MSG_PREFIX + "第【" + i + "】行匹配");}}} else {// 精准匹配 即两份excel的每一行每一列格式相同for (int i = 1; i <= sourceList.size(); i++) {// 表格1的第i行List<String> list1 = sourceList.get(i - 1);// 表格2的第i行List<String> list2 = targetList.get(i - 1);// 精准匹配 即两份excel的每一行每一列都完全匹配// list2.equals(list1)或list1.hashCode() == list2.hashCode()if (list1.hashCode() == list2.hashCode()) {log.info("------第【{}】行, 匹配------", i);this.successMsg.add(MSG_PREFIX + "第【" + i + "】行, 匹配");} else {log.info("------第【{}】行, 不匹配------", i);this.errorMsg.add(MSG_PREFIX + "第【" + i + "】行, 不匹配");result = false;}}}} catch (IndexOutOfBoundsException ex) {log.error("两个表格可能存在行数不一致,导致sourceList与targetList长度不一致!");result = false;} finally {log.info("------------------------比较结束------------------------");}return result;}/*** 读取excel 封装成集合* 该程序需要传入一份excel 和excel的列数 行数由程序自动检测* 注意:该方法统计的行数是默认第一行为title 不纳入统计的** @return excel的集合*/private List<List<String>> readExcel(int sheetNum, InputStream inputStream, String type) {List<List<String>> list = new ArrayList<>();// 建需要读取的excel文件写入streamtry (Workbook workbook = WorkbookFactory.create(inputStream)) {// 指向sheet下标为0的sheet 即第一个sheet 也可以按在sheet的名称来寻找Sheet sheet = workbook.getSheetAt(sheetNum);// 获取sheet1中的总行数int rowTotalCount = sheet.getLastRowNum();//获取总列数int columnCount = 0;for (int i = 0; i <= rowTotalCount; i++) {// 获取第i列的row对象Row row = sheet.getRow(i);ArrayList<String> listRow = new ArrayList<>();int physicalNumberOfCells = row.getPhysicalNumberOfCells();if (physicalNumberOfCells >= columnCount) {columnCount = physicalNumberOfCells;}for (int j = 0; j < physicalNumberOfCells; j++) {//下列步骤为判断cell读取到的数据是否为null 如果不做处理 程序会报错String cell = null;//如果未null则加上""组装成非null的字符串if (row.getCell(j) == null) {cell = row.getCell(j) + "";listRow.add(cell);//如果读取到额cell不为null 则直接加入	listRow集合} else {cell = row.getCell(j).toString();listRow.add(cell);}}list.add(listRow);}log.info("Excel【{}】中【{}】页签共计:{}行,{}列(列为最大列)", type, sheet.getSheetName(), rowTotalCount + 1, columnCount);} catch (IOException e) {log.error("读取excel sheet{}失败: {}", sheetNum, e.getMessage());}return list;}/*** ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓私有属性方法↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/public List<String> getDifferential() {return differential;}private void setDifferential(List<String> differential) {this.differential = differential;}public List<String> getErrorMsg() {return errorMsg;}private void setErrorMsg(List<String> errorMsg) {this.errorMsg = errorMsg;}public List<String> getSuccessMsg() {return successMsg;}private void setSuccessMsg(List<String> successMsg) {this.successMsg = successMsg;}
}

相关文章:

Java比较两个Excel是否内容一致

领导每天让比较两个Excel中的内容&#xff0c;为了节省工作效率多摸鱼&#xff0c;就写了个java接口&#xff0c;通过上传两个文件 进行代码比较得到详细的比较结果(这个需要自己根据日志二开) 目前只实现了比较功能 话不多说直接上代码&#xff0c;具体看注释 package com.yx…...

UniApp入门教程

UniApp X 是一种用于构建跨平台应用程序的框架&#xff0c;它基于 Vue.js 并通过 UniApp 技术栈支持多种平台&#xff0c;如微信小程序、支付宝小程序、H5、Android 和 iOS。以下是 UniApp X 的一些关键特点和基础知识&#xff1a; UniApp X 的特点 跨平台支持&#xff1a; 可…...

Vue.js中使用Element UI实现动态表单项管理及验证

在Vue.js项目中&#xff0c;表单是与用户交互的重要部分&#xff0c;特别是在需要动态管理表单项的场景下&#xff0c;如何优雅地实现添加、删除、上移、下移及验证功能变得尤为重要。本文将详细介绍如何使用Element UI来实现一个包含动态表单项管理以及验证功能的表单。 效果…...

一插U盘就提示格式化?原因、恢复与预防全攻略

一、现象直击&#xff1a;U盘插入电脑即提示格式化 在日常的工作与生活中&#xff0c;U盘作为重要的数据存储和传输工具&#xff0c;被广泛应用于各类场景。然而&#xff0c;有时当我们满怀期待地将U盘插入电脑时&#xff0c;却会遭遇一个令人头疼的问题——系统弹出提示框&am…...

云电脑使用教程标准版

云电脑&#xff0c;也称为云桌面&#xff0c;是一种通过互联网连接远程服务器&#xff0c;使用虚拟桌面环境来执行计算任务的技术。川翔云电脑通过创建软件镜像&#xff0c;让用户能够快速启动并使用预配置的软件和资料&#xff0c;提供高效且经济的云服务。相较于公有云服务&a…...

浏览器服务端文件下载控制(安全阻止、文件浏览器打开还是下载行为控制)

文章目录 简介Chrome已阻止不安全内容下载PDF直接打开txt、xml、js文件被自动打开了而不是下载阿里OSS设置response header阿里OSS修改metadata 简介 随着浏览器的发展&#xff0c;有很多安全方面的限制&#xff0c;对我们的文件下载行为产生了很大的影响。 在JavaScript下载…...

机器学习——量子机器学习

量子机器学习: 未来的机器学习方法 量子计算和机器学习的结合为计算科学带来了前所未有的前景。量子机器学习(QML)正在迅速发展&#xff0c;目标是利用量子计算的优势来处理传统计算机无法高效解决的问题。本文将深入探讨量子机器学习的基本概念、量子计算的关键技术、具体的量…...

[Linux] 创建可以免密登录的SFTP用户

本文主要包含: 创建新用户创建密钥对用于免密登录新用户将新建用户改造为SFTP用户为SFTP上传数据设置限速 1. 创建新用户 sudo useradd sftp_user sudo passwd sftp_user # 输入密码2. 创建密钥对 参考这篇文章 [Linux] 生成 PEM 密钥对实现服务器的免密登录 3. 将新建用户…...

【部署篇】Redis-03主从模式部署(源码方式安装)

一、准备主机 主从模式只是解决了数据备份容灾并不能解决单点故障问题&#xff0c;生产环境中需要在主从模式基础上增加哨兵&#xff0c;实现主节点宕机时自动将其中一个重节点设置为新的主节点。 主机IP角色说明192.168.128.31master&#xff0c;主节点可读写。192.168.128…...

C/C++语言基础--C++四大类型转换讲解

本专栏目的 更新C/C的基础语法&#xff0c;包括C的一些新特性 前言 通过前面几节课&#xff0c;我们学习了抽象、封装、继承、多态、异常等概念&#xff0c;这一篇我们将继续学习C的类型转换&#xff0c;和C语言还有很大区别的&#xff1b;在本节课最后&#xff0c;也简要说…...

KafKa 集群【docker compose】

文章目录 主机准备部署编辑 docker-compose.ymlcontrollerbroker生成cluster_id 一篇完整的 docker-compose.yml 文件查看集群状态使用 kafka-ui 查看拉取 kafka-ui添加集群查看集群状态 使用命令行查看 配置讲解controllerbroker 主机准备 IPcontroller idbroker id192.168.1…...

【工具篇】MLU运行XInference部署手册

文章目录 前言一、平台环境准备二、代码下载三、安装部署1.正常pip 安装 四、运行结果展示1.如果界面404或没有东西请这样做2.运行效果 前言 Xorbits Inference&#xff08;Xinference&#xff09;是一个功能强大、用途广泛的库&#xff0c;旨在为语言、语音识别和多模态模型提…...

计算机网络:数据链路层 —— 扩展共享式以太网

文章目录 共享式以太网共享式以太网存在的问题在物理层扩展以太网扩展站点与集线器之间的距离扩展共享式以太网的覆盖范围和站点数量 在链路层扩展以太网网桥的主要结构网桥的基本工作原理透明网桥自学习和转发帧生成树协议STP 共享式以太网 共享式以太网是当今局域网中广泛采…...

平安养老险深圳分公司:创新养老服务,深入践行金融为民

党的二十届三中全会《决定》提出&#xff1a;“积极发展科技金融、绿色金融、普惠金融、养老金融、数字金融&#xff0c;加强对重大战略、重点领域、薄弱环节的优质金融服务。” 为经济社会发展提供高质量服务&#xff0c;更好满足人民日益增长的美好生活需要&#xff0c;金融…...

静态站点生成器哪家强?

有一种方法&#xff0c;让你写好文档后&#xff0c;快速地让同事、用户和合作伙伴看到&#xff0c;这就是静态站点生成器。 静态站点生成器是一种软件&#xff0c;用于创建不需要服务器端脚本的网站。这些网站由纯HTML文件组成&#xff0c;可能还包括CSS和JavaScript来增强功…...

从0开始部署优化虚拟机

一&#xff0c;vm workstation 安装 CentOS-7 忽略 二、查看虚拟机IP ip address 得到 192.168.196.128/24 宿主机进行Ping测试 C:\Users\Administrator>ping 192.168.196.128正在 Ping 192.168.196.128 具有 32 字节的数据: 来自 192.168.196.128 的回复: 字节32 时间…...

录屏有道, 四款必备录屏工具推荐!

制作教程视频、游戏直播或是远程会议记录等都需要录屏&#xff0c;那么到底应该怎么录屏呢&#xff1f;接下来就给大家介绍几个好用的录屏工具 Foxit REC 直达链接&#xff1a;www.foxitsoftware.cn/REC/ 操作教程&#xff1a;立即获取 Foxit REC以其强大的功能、简洁的界面…...

5G NR:UE初始接入信令流程浅介

UE初始接入信令流程 流程说明 用户设备&#xff08;UE&#xff09;向gNB-DU发送RRCSetupRequest消息。gNB-DU 包含 RRC 消息&#xff0c;如果 UE 被接纳&#xff0c;则在 INITIAL UL RRC MESSAGE TRANSFER 消息中包括为 UE 分配的低层配置&#xff0c;并将其传输到 gNB-CU。IN…...

探索 Spring AI:Java 开发者的 AI 应用开发新利器

在当今这个由人工智能驱动的时代&#xff0c;AI 技术正在以前所未有的速度改变着我们的工作和生活方式。对于 Java 开发者来说&#xff0c;将 AI 能力集成到他们的应用程序中&#xff0c;已经成为了一个迫切的需求。阿里云开源的 Spring AI Alibaba 框架&#xff0c;正是为了满…...

Linux历史

Linux 于 1991 年由芬兰学生 Linus Torvalds 作为个人项目开始&#xff0c;旨在创建一个新的免费操作系统内核。在其历史发展中&#xff0c;Linux 内核经历了持续的增长。自 1991 年首次发布源代码以来&#xff0c;Linux 内核从少量的 C 语言文件&#xff0c;且受限于禁止商业发…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件&#xff0c;用于在原生应用中加载 HTML 页面&#xff1a; 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期&#xff0c;让读者对此有深刻印象。 目录 ​…...

十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建

【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...

【UE5 C++】通过文件对话框获取选择文件的路径

目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 &#xff0c;这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器&#xff0c;右键点击 .uproject 文件&#xff0c;选择 "Generate Visual Studio project files"&#xff0c;重…...