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;
}
}
}
我正在使用 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;
}
}
}