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、不同表头等),可以进一步扩展此代码。

相关推荐
星火开发设计2 小时前
枚举类 enum class:强类型枚举的优势
linux·开发语言·c++·学习·算法·知识
喜欢吃燃面7 小时前
Linux:环境变量
linux·开发语言·学习
徐徐同学8 小时前
cpolar为IT-Tools 解锁公网访问,远程开发再也不卡壳
java·开发语言·分布式
LawrenceLan8 小时前
Flutter 零基础入门(二十六):StatefulWidget 与状态更新 setState
开发语言·前端·flutter·dart
m0_748229998 小时前
Laravel8.X核心功能全解析
开发语言·数据库·php
qq_192779878 小时前
C++模块化编程指南
开发语言·c++·算法
Mr.朱鹏9 小时前
Nginx路由转发案例实战
java·运维·spring boot·nginx·spring·intellij-idea·jetty
代码村新手9 小时前
C++-String
开发语言·c++
qq_401700419 小时前
Qt 中文乱码的根源:QString::fromLocal8Bit 和 fromUtf8 区别在哪?
开发语言·qt