【问题标题】:How can Apache POI use formulas in streaming mode?Apache POI 如何在流模式下使用公式?
【发布时间】:2017-09-25 10:40:24
【问题描述】:

我正在使用 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,尽管在输入行中公式显示正确。

感谢任何提示。

【问题讨论】:

    标签: java apache-poi formulas


    【解决方案1】:

    它适用于 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);
    

    希望这两个中的一个也能对你有用。

    【讨论】:

    • 非常感谢! evaluateAll() 成功了。但我将保留这两个提议的陈述。
    • 补充几点说明:在某些情况下,evaluateAll() 可能会抛出异常,表示一行已被刷新。在这种情况下,可以使用 evaluateFormulaCellEnum(cell)。
    【解决方案2】:

    答案没有回答为什么只有在将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;
      }
    
     }
    
    }
    

    【讨论】:

    • 谢谢@Axel Richter;看起来很有趣。是的,SXSSFRow 没有任何受保护的方法或成员,因此扩展它的唯一合理方法是通过反射。我看到 SXXSFCell .getCachedFormulaResultTypeEnum() 已被弃用;也许这意味着 POI 开发人员目前没有研究该主题 - 或者不鼓励使用它。
    • @Renardo:我提供的解决方法不是解决方案,而只是一种解决方法。解决方案是更改SXSSFCell 的代码,使其使用值0 作为未计算公式的缓存公式结果。但是apache poi 的变更管理也很奇怪。截至今天,apidocs 声明 SXSSFCell.getCachedFormulaResultTypeEnum() 已弃用,SXSSFCell.getCachedFormulaResultType() 返回 CellType。但在实际版本 3.17 使用 getCachedFormulaResultTypeEnum() 并且getCachedFormulaResultType() 返回一个int
    【解决方案3】:

    当然我应该提到我使用 LibreOffice。我现在发现 LibreOffice 故意不会从 Excel 创建的工作表中重新计算公式,它会将 POI 工作表视为 Excel 创建的。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多