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

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

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

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

        // 行数不一致直接返回false
        if (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文件写入stream

        try (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;
    }
}
相关推荐
爱编程的鱼9 分钟前
OpenCV Python 绑定:原理与实战
c语言·开发语言·c++·python
这周也會开心16 分钟前
云服务器安装JDK、Tomcat、MySQL
java·服务器·tomcat
hrrrrb1 小时前
【Spring Security】Spring Security 概念
java·数据库·spring
小信丶1 小时前
Spring 中解决 “Could not autowire. There is more than one bean of type“ 错误
java·spring
sdgsdgdsgc1 小时前
Next.js企业级应用开发:SSR、ISR与性能监控方案
开发语言·前端·javascript
周杰伦_Jay2 小时前
【Java虚拟机(JVM)全面解析】从原理到面试实战、JVM故障处理、类加载、内存区域、垃圾回收
java·jvm
rit84324995 小时前
基于MATLAB的模糊图像复原
开发语言·matlab
fie88895 小时前
基于MATLAB的声呐图像特征提取与显示
开发语言·人工智能
程序员小凯6 小时前
Spring Boot测试框架详解
java·spring boot·后端
豐儀麟阁贵6 小时前
基本数据类型
java·算法