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;
    }
}
相关推荐
Ajiang28247353041 小时前
对于C++中stack和queue的认识以及priority_queue的模拟实现
开发语言·c++
幽兰的天空1 小时前
Python 中的模式匹配:深入了解 match 语句
开发语言·python
Theodore_10224 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
冰帝海岸5 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象5 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了6 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
----云烟----6 小时前
QT中QString类的各种使用
开发语言·qt
lsx2024066 小时前
SQL SELECT 语句:基础与进阶应用
开发语言
小二·6 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic6 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端