Java 实现 Excel 文件对比与数据填充

新老数据的维护工具,例如:A文件有a、b、c列共十条数据,B文件有a、b、c、d列数据共15条数据(其中有包含A的一些数据)如何快速的将A里有的数据放入到B中(长点心吧!可别一条条比对着录入数据)

下面是一个完整的 Java 实现,

使用 Apache POI 库处理 Excel 文件,对比 A、B 文件中的"测点标识"列(定位到相同的数据行),并将 A 文件中的"标签值"列填充到 B 文件对应的位置。

1. 添加 Maven 依赖
XML 复制代码
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>5.2.3</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.3</version>
</dependency>
2. 完整代码实现
java 复制代码
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.Map;

public class ExcelDataComparator {

    public static void main(String[] args) {
        String fileAPath = "path/to/FileA.xlsx";
        String fileBPath = "path/to/FileB.xlsx";
        String outputPath = "path/to/OutputFile.xlsx";

        try {
            // 1. 读取文件A的数据(测点标识 -> 标签值)
            Map<String, String> pointToTagMap = readFileA(fileAPath);

            // 2. 处理文件B并填充数据
            processFileB(fileBPath, outputPath, pointToTagMap);

            System.out.println("数据处理完成!输出文件: " + outputPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取文件A,构建测点标识到标签值的映射
     */
    private static Map<String, String> readFileA(String filePath) throws Exception {
        Map<String, String> map = new HashMap<>();
        FileInputStream fis = new FileInputStream(filePath);
        Workbook workbook = new XSSFWorkbook(fis);
        Sheet sheet = workbook.getSheetAt(0); // 假设使用第一个工作表

        // 获取表头行确定列索引
        Row headerRow = sheet.getRow(0);
        int pointIdIndex = -1;
        int tagValueIndex = -1;

        for (Cell cell : headerRow) {
            String headerName = cell.getStringCellValue().trim();
            if ("测点标识".equals(headerName)) {
                pointIdIndex = cell.getColumnIndex();
            } else if ("标签值".equals(headerName)) {
                tagValueIndex = cell.getColumnIndex();
            }
        }

        if (pointIdIndex == -1 || tagValueIndex == -1) {
            throw new RuntimeException("文件A中缺少必要的表头列");
        }

        // 遍历数据行
        for (int i = 1; i <= sheet.getLastRowNum(); i++) {
            Row row = sheet.getRow(i);
            if (row == null) continue;

            String pointId = getCellStringValue(row.getCell(pointIdIndex));
            String tagValue = getCellStringValue(row.getCell(tagValueIndex));

            if (pointId != null && !pointId.isEmpty()) {
                map.put(pointId, tagValue);
            }
        }

        workbook.close();
        fis.close();
        return map;
    }

    /**
     * 处理文件B并填充数据
     */
    private static void processFileB(String inputPath, String outputPath, 
                                   Map<String, String> pointToTagMap) throws Exception {
        FileInputStream fis = new FileInputStream(inputPath);
        Workbook workbook = new XSSFWorkbook(fis);
        Sheet sheet = workbook.getSheetAt(0);

        // 获取表头行确定列索引
        Row headerRow = sheet.getRow(0);
        int pointIdIndex = -1;
        int tagValueIndex = -1;

        for (Cell cell : headerRow) {
            String headerName = cell.getStringCellValue().trim();
            if ("测点标识".equals(headerName)) {
                pointIdIndex = cell.getColumnIndex();
            } else if ("标签值".equals(headerName)) {
                tagValueIndex = cell.getColumnIndex();
            }
        }

        if (pointIdIndex == -1) {
            throw new RuntimeException("文件B中缺少'测点标识'列");
        }

        // 如果文件B没有"标签值"列,可以创建(这里假设已有该列)
        if (tagValueIndex == -1) {
            tagValueIndex = headerRow.getLastCellNum();
            Cell newHeaderCell = headerRow.createCell(tagValueIndex);
            newHeaderCell.setCellValue("标签值");
        }

        // 遍历数据行并填充
        for (int i = 1; i <= sheet.getLastRowNum(); i++) {
            Row row = sheet.getRow(i);
            if (row == null) continue;

            String pointId = getCellStringValue(row.getCell(pointIdIndex));
            if (pointId == null || pointId.isEmpty()) continue;

            // 从文件A的映射中获取对应的标签值
            String tagValue = pointToTagMap.get(pointId);
            if (tagValue != null) {
                Cell targetCell = row.getCell(tagValueIndex, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
                targetCell.setCellValue(tagValue);
            }
        }

        // 保存修改后的文件
        FileOutputStream fos = new FileOutputStream(outputPath);
        workbook.write(fos);
        workbook.close();
        fos.close();
    }

    /**
     * 安全获取单元格字符串值
     */
    private static String getCellStringValue(Cell cell) {
        if (cell == null) return "";

        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue().trim();
            case NUMERIC:
                return String.valueOf((int) cell.getNumericCellValue());
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                return cell.getCellFormula();
            default:
                return "";
        }
    }
}

关键点说明

  1. 数据结构:

    • 使用 Map<String, String> 存储文件A中的"测点标识"->"标签值"映射关系
  2. 列索引定位:

    • 动态查找"测点标识"和"标签值"所在的列索引
  3. 单元格处理:

    • getCellStringValue() 方法处理各种类型的单元格数据

    • 使用 Row.MissingCellPolicy.CREATE_NULL_AS_BLANK 处理可能为空的单元格

  4. 文件处理:

    • 读取文件A构建映射关系

    • 读取文件B并修改数据

    • 输出到新文件(避免修改原始文件)

使用示例

假设:

  • 文件A.xlsx:

    测点标识 标签值
    P001 温度
    P002 压力
  • 文件B.xlsx:

    测点标识 其他数据 标签值
    P001 xxx
    P003 yyy

运行程序后,输出文件将变为:

测点标识 其他数据 标签值
P001 xxx 温度
P003 yyy

注意事项

  1. 文件格式支持:代码使用 .xlsx 格式(POI的XSSF)

  2. 性能优化:对于大文件,可以考虑使用 SXSSFWorkbook

  3. 错误处理:实际应用中应添加更完善的异常处理

  4. 表头检查:确保两个文件都有"测点标识"列

如果需要处理更复杂的情况(如多sheet、不同表头等),可以进一步扩展此代码。

相关推荐
乌暮17 小时前
综合案例:宠物电商系统
java·开发语言·spring·mvc·restful·1024程序员节
大文说跨境17 小时前
亚马逊多店铺的图片素材怎么批量去重:EXIF 与哈希的处理流程
开发语言·php
VimorphTech17 小时前
一段字幕太长,怎样拆句并安排显示时间?
java·spring boot
万联WANFLOW17 小时前
Kimi K3.1 Pro 接口接入指南:模型选型、费用计算与零数据保留
java·spring boot·python·安全·spring·大模型
织码weavecodes17 小时前
新闻资讯模块设计:内容发布、阅读计数与附件鉴权
java·服务器·数据库
学到老活到老17 小时前
Spring 事务怎样生效?为什么加了 @Transactional 仍不回滚?
java·spring
学到老活到老17 小时前
Spring 怎样创建 Bean?@Autowired 在哪一步生效?
java·spring
用户1129361121917 小时前
我扫了项目里 739 个 Controller,发现最危险的越权漏洞不是没认证
java
小羊没烦恼!17 小时前
分布式事务 | 使用DTM 的Saga 模式
java·linux·运维·数据库·wpf
JPower_mr.g17 小时前
SmartCall v1.0.9 发布:智能体知识库上线,让 AI 客服先查资料再回答
java·ide·人工智能·macos·架构·开源·xcode