Apache POI 如何在流模式下使用公式?

How can Apache POI use formulas in streaming mode?

我正在使用 Apache POI 3.17(当前)。当我使用 HSSFCell.setFormula() 插入像 "A1+17" 这样的公式时,它起作用了。当我在流模式下执行相同操作时,使用 SXSSFCell.setFormula() 公式在输入行中出现(带有前导“=”)但单元格中显示的结果始终为 0。

我尝试了单元格类型 NUMERIC 和 FORMULA。这是我最小的无效示例:

 final SXSSFWorkbook wb = new SXSSFWorkbook(); 
 final SXSSFSheet sheet = wb.createSheet("Test-S");
 final SXSSFRow row = sheet.createRow(0);

 final SXSSFCell cell1 = row.createCell(0);
 cell1.setCellType(CellType.NUMERIC);
 cell1.setCellValue(124);

 final SXSSFCell formulaCell1 = row.createCell(1);
 formulaCell1.setCellType(CellType.FORMULA);
 formulaCell1.setCellFormula("A1 + 17");

 final SXSSFCell formulaCell2 = row.createCell(2);
 formulaCell2.setCellType(CellType.NUMERIC);
 formulaCell2.setCellFormula("A1+18");

 FileOutputStream os = new FileOutputStream("/tmp/test-s.xlsx");
 wb.write(os);
 wb.close();
 os.close();

三个单元格显示为 124/0/0,但在输入行中公式显示正确。

如有任何提示,我们将不胜感激。

它适用于 Excel 2016,当我打开示例文件时,我在单元格中得到了正确的结果。 Excel 的旧版本处理方式可能略有不同,请尝试使用以下两件事强制计算公式

// evaluate all formulas and store cached results
wb.getCreationHelper().createFormulaEvaluator().evaluateAll();
// suggest to Excel to recalculate the formulas itself as well
sheet.setForceFormulaRecalculation(true);

希望这两者之一也能为您所用。

当然我应该提到我使用 LibreOffice。我现在发现 LibreOffice 有意不从 Excel 创建的 sheet 重新计算公式,它认为 POI sheets 是 Excel 创建的。

参见https://ask.libreoffice.org/en/question/12165/calc-auto-recalc-does-not-work/

更改 LibreOffice 设置(工具 – 选项 – LibreOffice Calc – 公式 – 文件加载时重新计算)有帮助。

答案没有回答为什么只有 SXSSFCell 用作公式单元格时才会出现 OpenOffice/Libreoffice 的问题。当使用 XSSFCell 作为公式单元格时,它不会出现。

答案是 SXSSFCell 总是 使用单元格值,即使根本没有计算公式。最糟糕的是,如果根本没有计算公式,它会使用值 0(零)。这是对数学中值 0 的基本误用。值 0 明确表示 not 表示没有值或存在未知值。这意味着只有值 0,没有别的。因此值 0 应该 而不是 用作未评估公式的缓存公式结果。相反,在计算公式之前,应使用 no 值。与 XSSFCell 完全一样。

所以真正正确的答案必须是 apache poi 应该更正他们的 SXSSFCell 代码。

在此之前的解决方法:

import java.io.FileOutputStream;

import org.apache.poi.xssf.streaming.*;

import org.apache.poi.ss.usermodel.CellType;

import java.lang.reflect.Field;

import java.util.TreeMap;

public class CreateExcelSXSSFFormula {

 public static void main(String[] args) throws Exception {

  SXSSFWorkbook wb = new SXSSFWorkbook();

  SXSSFSheet sheet = wb.createSheet("Test-S");
  SXSSFRow row = sheet.createRow(0);

  SXSSFCell cell = row.createCell(0);
  cell.setCellValue(124);

  SXSSFFormulaonlyCell formulacell = new SXSSFFormulaonlyCell(row, 1);
  formulacell.setCellFormula("A1+17");

  cell = row.createCell(2);
  cell.setCellFormula("A1+17");

  formulacell = new SXSSFFormulaonlyCell(row, 3);
  formulacell.setCellFormula("A1+18");

  cell = row.createCell(4);
  cell.setCellFormula("A1+18");

  wb.write(new FileOutputStream("test-s.xlsx"));
  wb.close();
  wb.dispose();

 }

 private static class SXSSFFormulaonlyCell extends SXSSFCell {

  SXSSFFormulaonlyCell(SXSSFRow row, int cellidx) throws Exception {
   super(row, CellType.BLANK);
   Field _cells = SXSSFRow.class.getDeclaredField("_cells"); 
   _cells.setAccessible(true);
   @SuppressWarnings("unchecked") //we know the problem and expect runtime error if it possibly occurs
   TreeMap<Integer, SXSSFCell> cells = (TreeMap<Integer, SXSSFCell>)_cells.get(row);
   cells.put(cellidx, this);
  }

  @Override
  public CellType getCachedFormulaResultTypeEnum() {
   return CellType.BLANK;
  }

 }

}