在使用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>
类型t
是inlineStr
。单元格值直接是字符串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)
将不再具有这种效果。