1. 简介

在 Java 中处理 Excel 文件时,我们通常关心的是单元格的实际值,比如用于数据计算或生成报表。但实际开发中经常会遇到一些单元格里存的不是原始数据,而是 Excel 公式(Formula)。这时候如果直接读取,拿到的是公式字符串,而不是我们想要的结果值。

那怎么才能拿到这些公式单元格的计算结果呢?本文将介绍如何借助 Apache POI 库,正确读取 Excel 中公式单元格的最终值。

解决这个问题主要有两种方式:

获取单元格的缓存值(Cached Value)
在运行时动态计算公式(Formula Evaluation)

接下来我们分别来看这两种方案的实现和适用场景。


2. Maven 依赖

使用 Apache POI 操作 .xlsx 文件需要引入 poi-ooxml 模块:

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

最新版本可从 Maven Central 获取。⚠️ 建议保持版本更新,避免已知 Bug。


3. 获取单元格的缓存值

Excel 在保存文件时,会对包含公式的单元格同时保存两个内容:

  • 公式本身(如 =A1+B1
  • 公式的上一次计算结果(即缓存值)

这个缓存值就是我们常说的“last cached value”。当我们打开一个已保存的 Excel 文件时,POI 可以直接读取这个缓存值,而无需重新计算公式。

⚠️ 注意:这个值可能不是最新的!如果 Excel 文件在外部被修改过但未重新计算(比如关闭了自动计算),缓存值就会过时。

使用方式

FileInputStream inputStream = new FileInputStream(new File("temp.xlsx"));
Workbook workbook = new XSSFWorkbook(inputStream);
Sheet sheet = workbook.getSheetAt(0);

CellAddress cellAddress = new CellAddress("C2");
Row row = sheet.getRow(cellAddress.getRow());
Cell cell = row.getCell(cellAddress.getColumn());

if (cell.getCellType() == CellType.FORMULA) {
    switch (cell.getCachedFormulaResultType()) {
        case BOOLEAN:
            System.out.println(cell.getBooleanCellValue());
            break;
        case NUMERIC:
            System.out.println(cell.getNumericCellValue());
            break;
        case STRING:
            System.out.println(cell.getRichStringCellValue());
            break;
    }
}

📌 关键点说明:

  • cell.getCellType() == CellType.FORMULA:判断是否为公式单元格
  • getCachedFormulaResultType():获取缓存值的数据类型
  • 根据类型调用对应的方法读取值,如 getNumericCellValue()getStringCellValue()

✅ 优点:性能高,无需计算
❌ 缺点:依赖文件保存时的状态,可能不准确


4. 运行时计算公式获取值

如果你需要确保读取的是最新结果,尤其是程序中会动态修改 Excel 数据,就必须在运行时重新计算公式。

Apache POI 提供了 FormulaEvaluator 类,专门用于动态求值。

使用方式

// 已有 Workbook 初始化
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); 

// 已有 Sheet, Row, Cell 初始化
if (cell.getCellType() == CellType.FORMULA) {
    switch (evaluator.evaluateFormulaCell(cell)) {
        case BOOLEAN:
            System.out.println(cell.getBooleanCellValue());
            break;
        case NUMERIC:
            System.out.println(cell.getNumericCellValue());
            break;
        case STRING:
            System.out.println(cell.getStringCellValue());
            break;
    }
}

📌 关键点说明:

  • createFormulaEvaluator():创建公式求值器
  • evaluateFormulaCell(cell):触发公式计算,返回结果类型(CellType
  • 计算完成后,cell 对象本身的值会被更新为最新结果

✅ 优点:结果准确,适合动态场景
❌ 缺点:性能开销大,尤其公式复杂或数量多时


5. 如何选择?

场景 推荐方案 理由
✅ 读取静态、已保存的 Excel 文件 缓存值 简单粗暴,性能好,无需计算
✅ 程序会修改 Excel 数据并需读取最新结果 运行时计算 保证数据一致性
⚠️ 文件来源不可控,不确定是否计算过 运行时计算 避免踩坑缓存过期问题

💡 小技巧:如果你不确定,**优先使用 FormulaEvaluator**,虽然慢一点,但结果靠谱。


6. 总结

本文介绍了两种读取 Excel 公式单元格实际值的方法:

  • 读缓存值:适合静态文件,性能优先
  • 运行时计算:适合动态修改,准确性优先

选择哪种方式,关键看你的业务场景是否涉及数据变更。别被“看起来能跑”迷惑,生产环境踩过缓存坑的不在少数。

完整示例代码已托管至 GitHub:https://github.com/eugenp/tutorials/tree/master/apache-poi


原始标题:Read Excel Cell Value Rather Than Formula With Apache POI