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

相关推荐
雷羿 LexChien5 分钟前
C++内存泄漏排查
开发语言·c++
努力的小郑8 分钟前
Spring三级缓存硬核解密:二级缓存行不行?一级缓存差在哪?
java·spring·面试
水果里面有苹果12 分钟前
17-C#的socket通信TCP-1
开发语言·tcp/ip·c#
手握风云-14 分钟前
JavaEE初阶第七期:解锁多线程,从 “单车道” 到 “高速公路” 的编程升级(五)
java·开发语言
发仔12314 分钟前
使用Canal实现MySQL到Elasticsearch数据同步
java·后端
nightunderblackcat15 分钟前
进阶向:Python音频录制与分析系统详解,从原理到实践
开发语言·python·音视频
hello早上好31 分钟前
Spring AOP:从代理创建到切点匹配
java·后端·spring
psjasf131442 分钟前
使用Ideal创建一个spring boot的helloWorld项目
java·spring boot·后端
努力学习的小廉1 小时前
深入了解linux系统—— System V之消息队列和信号量
android·linux·开发语言
添乱1 小时前
「Java案例」猜数字游戏
java