You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:如何读取含不同表头及列数的多工作表Excel文件(不可改原文件)

解决方案:根据表头动态映射列到对象字段

你的核心问题是现有代码固定了列索引来赋值Employee对象,但不同工作表的表头和列位置不一致,所以我们需要先读取每个工作表的表头,建立「表头名称→列索引」的映射,再根据这个映射动态提取数据,完全不用修改原Excel文件。

具体实现思路

  1. 预定义映射规则:把所有可能对应Employee字段的表头文本和字段名关联起来(比如"员工姓名"、"EmpName"都对应empName)。
  2. 读取每个工作表的表头,生成当前表的「表头文本→列索引」映射表。
  3. 遍历数据行时,通过映射表找到目标字段对应的列,提取值并赋值给Employee对象,同时处理列缺失、单元格类型不一致的情况。

修改后的完整代码

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class ReadExcelFileAndStore { 
    // 自定义表头与Employee字段的映射关系,根据你的实际表头补充所有可能的名称
    private static final Map<String, String> COLUMN_MAPPING = new HashMap<>();
    static {
        // 所有对应empName的表头文本
        COLUMN_MAPPING.put("员工姓名", "empName");
        COLUMN_MAPPING.put("EmpName", "empName");
        COLUMN_MAPPING.put("姓名", "empName");
        // 所有对应extCode的表头文本
        COLUMN_MAPPING.put("外部编码", "extCode");
        COLUMN_MAPPING.put("ExtCode", "extCode");
        COLUMN_MAPPING.put("员工编码", "extCode");
    }

    public List<Employee> getTheFileAsObject(String filePath){
        List<Employee> employeeList = new ArrayList<>();
        // 使用try-with-resources自动关闭流,避免资源泄漏
        try (FileInputStream file = new FileInputStream(new File(filePath))) { 
            HSSFWorkbook workbook = new HSSFWorkbook(file); 
            int numberOfSheets = workbook.getNumberOfSheets(); 

            for(int i = 0; i < numberOfSheets; i++) { 
                HSSFSheet sheet = workbook.getSheetAt(i); 
                Iterator<Row> rowIterator = sheet.rowIterator(); 

                // 跳过空表
                if (!rowIterator.hasNext()) {
                    continue;
                }
                // 读取表头,构建当前表的表头索引映射
                Row headerRow = rowIterator.next();
                Map<String, Integer> headerIndexMap = new HashMap<>();
                Iterator<Cell> headerCellIterator = headerRow.cellIterator();
                while (headerCellIterator.hasNext()) {
                    Cell cell = headerCellIterator.next();
                    String headerText = getCellContent(cell).trim();
                    if (!headerText.isEmpty()) {
                        headerIndexMap.put(headerText, cell.getColumnIndex());
                    }
                }

                // 遍历数据行,动态填充Employee对象
                while (rowIterator.hasNext()) { 
                    Row row = rowIterator.next(); 
                    Employee employee = new Employee(); 

                    // 填充empName字段
                    Integer nameColIndex = getTargetColumnIndex(headerIndexMap, "empName");
                    if (nameColIndex != null && row.getCell(nameColIndex) != null) {
                        employee.setEmpName(getCellContent(row.getCell(nameColIndex)));
                    }

                    // 填充extCode字段,兼容数字和字符串类型的单元格
                    Integer codeColIndex = getTargetColumnIndex(headerIndexMap, "extCode");
                    if (codeColIndex != null && row.getCell(codeColIndex) != null) {
                        Cell codeCell = row.getCell(codeColIndex);
                        if (codeCell.getCellType() == CellType.NUMERIC) {
                            employee.setExtCode((int) codeCell.getNumericCellValue());
                        } else if (codeCell.getCellType() == CellType.STRING) {
                            try {
                                employee.setExtCode(Integer.parseInt(codeCell.getStringCellValue()));
                            } catch (NumberFormatException e) {
                                // 处理非数字的编码值,这里可以根据需求设默认值或跳过
                                employee.setExtCode(null);
                            }
                        }
                    }

                    employeeList.add(employee); 
                } 
            } 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
        return employeeList; 
    }

    // 辅助方法:统一获取单元格内容,兼容不同类型的单元格
    private String getCellContent(Cell cell) {
        if (cell == null) {
            return "";
        }
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                return String.valueOf(cell.getNumericCellValue());
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            default:
                return "";
        }
    }

    // 辅助方法:根据目标字段,从表头映射中找到对应的列索引
    private Integer getTargetColumnIndex(Map<String, Integer> headerIndexMap, String targetField) {
        for (Map.Entry<String, String> entry : COLUMN_MAPPING.entrySet()) {
            if (entry.getValue().equals(targetField) && headerIndexMap.containsKey(entry.getKey())) {
                return headerIndexMap.get(entry.getKey());
            }
        }
        return null; // 找不到对应列时返回null
    }
}

// 假设你的Employee实体类如下(可根据实际结构调整)
class Employee {
    private String empName;
    private Integer extCode;

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public Integer getExtCode() {
        return extCode;
    }

    public void setExtCode(Integer extCode) {
        this.extCode = extCode;
    }
}

关键细节说明

  • 灵活的表头适配:你只需要在COLUMN_MAPPING里补充所有可能的表头文本,不管4个工作表用了什么命名的表头,都能匹配到对应的Employee字段。
  • 异常安全处理:代码处理了空表、缺失列、单元格类型不匹配的情况,不会因为某张表结构特殊就抛出异常中断执行。
  • 资源自动管理:使用try-with-resources语法自动关闭文件流,避免手动关闭遗漏导致的资源泄漏。

内容的提问来源于stack exchange,提问作者Parkavi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:36:12