所以我使用Apache POI(最新稳定发布版本5.0.0中的POI -ooxml)并打开现有的用于编辑的Excel (XSLX)文件(它基本上是一个用于填充其他数据的模板文件)。我添加了多行新的数据,并再次导出Excel。一切正常,只要我只添加常规内容单元格。
现在,我有一个列,我想在其中添加一个公式单元格,我使用以下代码(为本例进行了简化,您可以放心,通常它会编译/运行并在最后生成一个填充的Excel文件)来完成此操作:
File excelFileToRead = new File(<some filename here>);
InputStream inp = new FileInputStream(excelFileToRead);
Workbook wb = WorkbookFactory.create(inp);
Sheet sheet = wb.getSheetAt(0);
Row dateRangeRow = sheet.getRow(0);
// fill first cell with some date
Cell cell = row.getCell(0);
if(cell == null) row.createCell(0)
Date someDate = new Date();
cell.setCellValue(someDate);
// add formula to second cell to display the week number
Cell formCell = row.getCell(1);
if(formCell == null) row.createCell(1);
cell.setCellFormula("WEEKNUM(A1)");
// evaluate all formula fields before saving
XSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
//some routine to save as a file follows here, not exactly relevant here
一般,这没问题。第一个单元格被创建并填充为今天的日期,第二个单元格也被创建为公式单元格。
现在问题来了:当我打开Excel电子表格时,我可以看到数据,而在公式单元格中,我只能看到"#WERT"(使用德文Excel,我假设在英文版本中它会显示类似"#VALUE")。
当我简单地单击Excel中的公式编辑器并再次删除焦点时,它会正确地计算公式,单元格会显示正确的周数。
我以前的一些问题与现有公式Excel我读,他们没有当我添加数据表更新,但这可能与调用XSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
固定由于某些原因,它不影响我自定义创建的公式单元格。
我还尝试在创建后单独评估新创建的公式单元格:
FormulaEvaluator formulaEvaluator = wb.getCreationHelper().createFormulaEvaluator();
formulaEvaluator.evaluateFormulaCell(formCell);
这里也没有变化。
你知道我的代码或方法有什么问题吗?
我使用Excel版本16.53 (Excel for Mac)顺便说一下,但我真的希望它与确切的Excel版本无关:-)
注意:我发现了一个旧线程(在POI 5.0.0发布之前的方式),似乎讨论了同样的问题,但在较旧的POI版本中,也如上所述,我遵循了在保存之前调用evaluateAllFormulaCells(…)的一般做法,甚至在每个公式单元创建后调用evaluateFormulaCell(cell):
这是由于apache poi
在计算WEEKNUM函数时的一个错误造成的。
如果省略[return_type]
,则ist总是计算为#VALUE
错误。但是,即使你设置了[return_type]
,它的计算结果也不总是正确的。
你可以看到如果你:
...
FormulaEvaluator formulaEvaluator = wb.getCreationHelper().createFormulaEvaluator();
CellValue cellValue = formulaEvaluator.evaluate(formCell);
System.out.println(cellValue);
...
如果A1
包含日期9/27/2021
,B1
包含公式=WEEKNUM(A1)
,则apache poi
FormulaEvaluator
将其计算为#VALUE
。如果B1
包含公式=WEEKNUM(A1,1)
,则apache poi
FormulaEvaluator
将其求值为39
,但Excel
将其求值为40
。
要解决这个错误,可以在打开文件时强制Excel
计算所有公式。这可以使用wb.setForceFormulaRecalculation(true);
来完成。然后Excel
计算公式,因此结果是正确的。
重现问题的完整示例:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.formula.BaseFormulaEvaluator;
import java.util.GregorianCalendar;
class CreateExcelFormulaWEEKNUM {
public static void main(String[] args) throws Exception {
try (
//Workbook wb = WorkbookFactory.create(new FileInputStream("./ExcelIn.xls")); FileOutputStream fileout = new FileOutputStream("./ExcelOut.xls");
Workbook wb = WorkbookFactory.create(new FileInputStream("./ExcelIn.xlsx")); FileOutputStream fileout = new FileOutputStream("./ExcelOut.xlsx");
) {
Sheet sheet = wb.getSheetAt(0);
Row row = sheet.getRow(0); if (row == null) row = sheet.createRow(0);
Cell cell = row.getCell(0); if (cell == null) cell = row.createCell(0);
cell.setCellValue(new GregorianCalendar(2021, 8, 27));
CellReference cellReference = new CellReference(cell);
Cell formCell = row.getCell(1); if(formCell == null) formCell = row.createCell(1);
formCell.setCellFormula("WEEKNUM(" + cellReference.formatAsString() + ")"); // FormulaEvaluator evaluates to #VALUE because of [return_type] is not set
//formCell.setCellFormula("WEEKNUM(" + cellReference.formatAsString() + ", 1)"); // FormulaEvaluator evaluates to 39 which is wrong as Excel evaluates to 40
FormulaEvaluator formulaEvaluator = wb.getCreationHelper().createFormulaEvaluator();
CellValue cellValue = formulaEvaluator.evaluate(formCell);
System.out.println(cellValue);
BaseFormulaEvaluator.evaluateAllFormulaCells(wb);
wb.setForceFormulaRecalculation(true);
wb.write(fileout);
}
}
}