Java Apache POI Bug



在使用Apache POI实现时,我遇到了一种奇怪的行为。我无法解释原因,所以如果有人能给出一些提示,我很乐意听到。对于我正在解决的问题,它甚至不是一个很大的障碍——在这一点上,它更多的是一件令人好奇的事情。因此:

public static void main(String[] args) throws EcatException, SQLException, IOException, Exception {
long ts = System.currentTimeMillis();
SXSSFWorkbook wb = new SXSSFWorkbook();
SXSSFSheet test = wb.createSheet("Test");
SXSSFRow r = test.createRow(0);
Cell c = r.createCell(0);
c.setCellValue("TEST");
wb.write(new FileOutputStream("D:/wb-" + ts + ".xlsx"));
wb.close();
XSSFWorkbook wb2 = new XSSFWorkbook("D:/wb-" + ts + ".xlsx");
XSSFSheet s = wb2.getSheet("Test");
s.getRow(0).getCell(0).setCellType(CellType.STRING);
System.out.println(s.getRow(0).getCell(0).getStringCellValue());
wb2.close();
}

如您所见,这将创建一个具有一行和一个值为"的单元格的SXSSFWorkbook;TEST";。然后再次打开工作簿,并将该单元格的内容打印到控制台。

我的期望是看到";TEST";在控制台上,但我没有。输出为空。

  • 如果我删除该行
s.getRow(0).getCell(0).setCellType(CellType.STRING);

输出如预期。

  • 如果我从使用SXSSFWorkbook切换到使用XSSFWorkbook,则输出如预期。

  • 最奇怪的是,如果我打开生成的xlsx文件,保存它并再次关闭它,然后运行上面代码的读取部分,输出是预期的。

有人对此有解释吗?顺便说一句,我尝试了不同版本的POI,每次都有相同的结果。

问题是SXSSFWorkbook默认使用内联字符串,因为这更适合流式处理方法。但当单元类型为CellType.STRING时,XSSFWorkbook期望字符串存储在共享字符串表中。

因此,在创建SXSSFWorkbook之后,sheet1.xml中的单元格XML看起来像

<c r="A1" t="inlineStr">
<is>
<t>TEST</t>
</is>
</c>

类型tinlineStr。单元格值直接是字符串TEST。

但在Cell.setCellType(CellType.STRING)之后,类型t被设置为s。这需要值是一个数字,它是共享字符串表中字符串的索引。但事实并非如此。这就是System.out.println(s.getRow(0).getCell(0).getStringCellValue());无法打印任何内容的原因。

您可以执行SXSSFWorkbook wb = new SXSSFWorkbook(null, 100, true, true)来强制SXSSFWorkbook也使用共享字符串表。但这将降低流方法的性能,因为所有字符串都需要存储在共享字符串表中,而不是直接存储在单元格中。

共享字符串表的好处是节省内存,因为当多个单元格使用同一字符串时,所有字符串只存储一次,并且只有它们的索引存储在单元格中。

Excel本身从不使用内联字符串而不是使用共享字符串表来存储工作簿。因此,在Excel中打开并重新保存后,内联字符串将被共享字符串表中字符串的索引所取代,并且单元格类型始终为s而不是inlineStr。这就是为什么Cell.setCellType(CellType.STRING)将不再具有这种效果。

最新更新