2017-09-25 131 views
1

我使用的是Apache POI 3.17(当前版本)。当我使用HSSFCell.setFormula()插入一个像“A1 + 17”这样的公式时,它可以工作。当我在流模式下使用SXSSFCell.setFormula()时,输入行中出现公式(带有前导“=”),但单元格中的显示结果始终为0.Apache POI如何在流模式下使用公式?

我试过用单元格类型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,虽然在输入行中公式显示正确。

任何提示表示赞赏。

回答

2

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

希望这两个中的一个将使它为你工作为好。

+0

非常感谢! evaluateAll()做了诀窍。但是我要保留两份提议的陈述。 – Renardo

+0

附加说明:在某些情况下,evaluateAll()可能会抛出异常,表示行已经被刷新。在这种情况下,可以使用evaluateFormulaCellEnum(cell)。 – Renardo

1

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

答案是SXSSFCell总是使用单元格值,即使该公式根本没有被评估。最糟糕的是,如果公式根本没有被评估,它使用值0(零)。这是数学中0值的根本滥用。值0显式确实是而不是意味着没有值或者存在未知值。这意味着有值0而没有别的。因此,值0应该是而不是可用作未评估公式的高速缓存公式结果。相反值应该使用,直到公式被评估。确切为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; 
    } 

} 

} 
+0

谢谢@Axel Richter;这看起来很有趣。是的,SXSSFRow没有任何受保护的方法或成员,因此扩展它的唯一合理方式是反思。我看到'SXXSFCell'。'getCachedFormulaResultTypeEnum()'被弃用;也许这意味着POI开发者目前没有在研究这个主题 - 或者说它的使用是令人沮丧的。 – Renardo

+0

@Renardo:我提供的解决方法是**不是**解决方案,而只是解决方法。解决方案是更改'SXSSFCell'的代码,以便**不**使用值0作为未评估公式的缓存公式结果。但是'apache poi'的变更管理也很奇怪。到目前为止,apidocs声明'SXSSFCell.getCachedFormulaResultTypeEnum()'为不推荐,'SXSSFCell.getCachedFormulaResultType()'返回'CellType'。但是在真正的3.17版本中**使用**'getCachedFormulaResultTypeEnum()'并且'getCachedFormulaResultType()'返回一个'int'。 –