首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Apache POI 讀取 Excel 合併單元格完整解決方案:識別、取值、實戰代碼

     編輯:關於C語言

前言

Excel 合併單元格是報表導入的常見痛點:合併區域內只有左上角儲存格有值,其餘為空。本文提供基於 Apache POI 的完整工具類,實現 合併區域識別、任意儲存格取真實值、流式處理大文件 三大功能,代碼可直接投入生產使用。

核心 API 速覽

類/方法用途
Sheet.getMergedRegions()獲取所有合併區域列表
CellRangeAddress表示一個合併區域(首行、末行、首列、末列)
sheet.getMergedRegion(i)獲取第 i 個合併區域
ca.getFirstRow() / getLastRow() / getFirstColumn() / getLastColumn()區域邊界

工具類完整實現

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import java.util.*;

public class ExcelMergeUtil {

    /** 獲取 Sheet 所有合併區域 */
    public static List getMergedRegions(Sheet sheet) {
        List list = new ArrayList<>();
        int count = sheet.getNumMergedRegions();
        for (int i = 0; i < count; i++) {
            list.add(sheet.getMergedRegion(i));
        }
        return list;
    }

    /**
     * 判斷儲存格是否屬於合併區域,是則返回左上角儲存格的值
     * @param mergedRegions 合併區域列表
     * @param cell 待判斷儲存格
     * @param sheet 所在 Sheet
     * @return 真實值(字符串),非合併區域返回空串
     */
    public static String getMergedCellValue(List mergedRegions,
                                            Cell cell, Sheet sheet) {
        int rowIdx = cell.getRowIndex();
        int colIdx = cell.getColumnIndex();

        for (CellRangeAddress ca : mergedRegions) {
            if (rowIdx >= ca.getFirstRow() && rowIdx <= ca.getLastRow() &&
                colIdx >= ca.getFirstColumn() && colIdx <= ca.getLastColumn()) {
                // 命中合併區域,取左上角儲存格
                Row firstRow = sheet.getRow(ca.getFirstRow());
                if (firstRow == null) return "";
                Cell firstCell = firstRow.getCell(ca.getFirstColumn());
                return getCellStringValue(firstCell);
            }
        }
        return ""; // 非合併區域
    }

    /** 通用儲存格取字符串值(含公式、數值、日期、布爾) */
    public static String getCellStringValue(Cell cell) {
        if (cell == null) return "";
        switch (cell.getCellTypeEnum()) {
            case STRING: return cell.getStringCellValue();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell))
                    return cell.getDateCellValue().toString();
                // 數值去科學計數法
                return new java.text.DecimalFormat("#.##########").format(cell.getNumericCellValue());
            case BOOLEAN: return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                try { return getCellStringValue(cell.getCachedFormulaResultTypeEnum() == CellType.NUMERIC
                        ? cell : cell); } catch (Exception e) { return cell.getCellFormula(); }
            case BLANK: return "";
            default: return "";
        }
    }

    /** 讀取行時自動填充合併值的示例 */
    public static Map readRowAsMap(Row row, Sheet sheet,
                                                   List mergedRegions,
                                                   List headers) {
        Map map = new LinkedHashMap<>();
        for (int i = 0; i < headers.size(); i++) {
            Cell cell = row.getCell(i, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
            String val = getCellStringValue(cell);
            if (val.isEmpty()) // 嘗試合併值
                val = getMergedCellValue(mergedRegions, cell, sheet);
            map.put(headers.get(i), val);
        }
        return map;
    }
}

流式讀取大文件 (SXSSF / XSSF streaming)

// 僅示例關鍵點,完整代碼需處理 SharedStringsTable
try (OPCPackage pkg = OPCPackage.open(file);
     XSSFReader reader = new XSSFReader(pkg);
     InputStream sheetStream = reader.getSheet("rId1")) {

    XMLReader parser = XMLReaderFactory.createXMLReader();
    SheetHandler handler = new SheetHandler(mergedRegions); // 自定義 SAX 處理器
    parser.setContentHandler(handler);
    parser.parse(new InputSource(sheetStream));
}

注意:流式讀取時合併區域信息在 sheet.xml 的 <mergeCells> 中,需在 SAX 解析階段預先收集。

常見坑與避坑

坑點現象解決
合併區域重疊POI 允許但 Excel 不建議按區域面積排序,優先匹配最小區域
左上角儲存格為空合併後刪除內容向上/向左尋找最近非空儲存格,或業務端規範模板
日期/數值格式化丟失直接 toString() 得到科學計數法用 DataFormatter 或 DecimalFormat
大文件 OOMXSSF 全量載入改用 SAX 事件流或 SXSSF 寫入

Maven 依賴 (POI 5.2+)

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.3</version>
</dependency>

總結

  1. 收集合併區域:sheet.getMergedRegions() 一次性獲取
  2. 判斷歸屬:行列索引落在區域範圍內
  3. 取左上角值:sheet.getRow(firstRow).getCell(firstCol)
  4. 統一值提取:DataFormatter 處理所有類型
  5. 大文件:SAX 流式解析 + 預收集合併信息

將上述工具類封裝為 Spring Bean,配合模板驗證註解,即可構建健壯的 Excel 導入服務。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved