如何将Excel单元格中的数字字符串作为字符串(而非数字)读取?

166
  1. 我有一个Excel文件,其中包含以下内容:

    • A1:SomeString

    • A2:2

    所有字段都设置为字符串格式。

  2. 当我在Java中使用POI读取文件时,它告诉我A2是数字单元格格式。

  3. 问题在于,A2中的值可以是2或2.0(我希望能够区分它们),因此我不能仅使用.toString()

我该怎么做才能将其作为字符串读取?

24个回答

346
将单元格类型设置为字符串
我之前遇到了同样的问题。在读取字符串值之前,我使用了cell.setCellType(Cell.CELL_TYPE_STRING);,这样无论用户如何格式化单元格,问题都得到了解决。

我想知道这是否会考虑格式?比如说,如果Excel文件总是显示2位小数,那么字符串值是否完全相同? - ryvantage
67
请注意,Apache POI javadocs 明确指出不要这样做! 正如它们所解释的那样,您应该使用 DataFormatter。 - Gagravarr
8
Gagravarr警告不要这样做是正确的!从文档中可以看到:“如果你想得到一个数字单元格的字符串值,请停止操作!这不是正确的方法。相反,为了获取数字、布尔或日期单元格的字符串值,请使用DataFormatter。”https://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/Cell.html#setCellType%28int%29 我自己也使用这种技术,直到我意外改变了我不打算更改的数据。(将类型设为字符串,读取值,将类型重新设为数字,再次读取并获得不同的数字值!) - Chris Finley
1
@Wil cell.setCellType(Cell.CELL_TYPE_STRING); 在 .xlsx 表格中无法正常工作。还有其他解决方案吗? - Meenaxi
6
请使用DataFormatter。Javadoc警告我们不要使用上述方法。 - Balu SKT
显示剩余9条评论

127

DataFormatter

我记得当你提出这个问题时,我们还没有这个类,但是现在有一个简单的答案。

你想要做的是使用DataFormatter类。你将一个单元格传递给它,它会尽力返回一个包含Excel显示该单元格内容的字符串。如果你传递给它一个字符串单元格,它会返回该字符串。如果你传递给它一个应用了格式规则的数值单元格,它会根据这些规则格式化数字并返回字符串。

对于你的情况,我假设数值单元格应用了整数格式规则。如果你要求DataFormatter格式化这些单元格,它会返回一个包含整数字符串的字符串。

此外,注意到很多人建议使用cell.setCellType(Cell.CELL_TYPE_STRING),但是Apache POI JavaDocs非常明确地指出你不应该这样做!调用setCellType会丢失格式,因为javadocs解释了唯一保留格式的转换为字符串的方法是使用DataFormatter类
使用这个类的一个简单示例:
DataFormatter dataFormatter = new DataFormatter();
String formattedCellStr = dataFormatter.formatCellValue(cell);

谢谢@Gagravarr,只有你的答案对我有用。在将2.2转换为2.2000000000000002时,<code>cell.setCellType(Cell.CELL_TYPE_STRING);</code>可以工作,但我想要2.2。它以字符串格式返回任何内容。 - ankush yadav
数据格式化器似乎无法处理公式单元格,它返回公式的字符串表示而不是值。 - gaurav5430
3
好的,我会尽力进行翻译并保持原意不变。以下是需要翻译的内容:只有一个小提示:请为这样的答案提供简短的代码片段,即使这些代码片段在提供的链接中已经说明了。 - BAERUS
@gaurav5430 是的,它与公式不兼容... 根据文档,当传递一个空单元格时,此方法将返回一个空字符串("")。公式类型单元格中的公式将不会被计算。 - SaratBhaswanth
这也不适用于具有数字单元格类型的大整数,因为DataFormatter在内部将其转换为double,然后再转换回String,因此存在失去精度和前导/尾随0的风险。 - Stik

66

以下代码适用于我所遇到的任何类型的单元格。

InputStream inp =getClass().getResourceAsStream("filename.xls"));
Workbook wb = WorkbookFactory.create(inp);
DataFormatter objDefaultFormat = new DataFormatter();
FormulaEvaluator objFormulaEvaluator = new HSSFFormulaEvaluator((HSSFWorkbook) wb);

Sheet sheet= wb.getSheetAt(0);
Iterator<Row> objIterator = sheet.rowIterator();

while(objIterator.hasNext()){

    Row row = objIterator.next();
    Cell cellValue = row.getCell(0);
    objFormulaEvaluator.evaluate(cellValue); // This will evaluate the cell, And any type of cell will return string value
    String cellValueStr = objDefaultFormat.formatCellValue(cellValue,objFormulaEvaluator);

}

9
工作得很好!我的建议是改变FormulaEvaluator的检索方式。Workbook类通过getCreationHelper().createFormulaEvaluator()方法提供了一个公式求值器。这样你的代码就不会与HSSFFormulaEvaluator类耦合。 - Vitor Santos
2
这应该是被接受的答案。谢谢@Vinayak - Mattiavelli
这个解决方案中 FormulaEvaluator 可以被简单地移除吗?它有什么作用吗? - P.Brian.Mackey
2
不需要调用objFormulaEvaluator.evaluate。这里没有使用它的返回值。 - Radu Simionescu

45

如果不希望修改单元格类型,我建议采取以下方法:

if(cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
    String str = NumberToTextConverter.toText(cell.getNumericCellValue())
}

NumberToTextConverter能够使用Excel的规则将双精度值正确转换为文本,而不会失去精度。


非常令人兴奋的建议!谢谢!它允许获取未转换的值,而不是将cellType设置为String。 - Gleb Egunov
我的输出结果为44007,但实际上我输入的是日期值25/06/2020。我做错了什么? - Vinay
@Vinay 对于日期,请使用 DateUtil。类似这样的代码 DateUtil.getLocalDateTime(row.getCell(1).getNumericCellValue()).toLocalDate() - Georgy Bolyuba

20

16

是的,这个完美地运作了。

推荐:

        DataFormatter dataFormatter = new DataFormatter();
        String value = dataFormatter.formatCellValue(cell);

old:

cell.setCellType(Cell.CELL_TYPE_STRING);

即便您从带有公式的cell中检索值时遇到问题,这仍然有效。


5
使用这个方法时,对于双精度浮点数需要特别小心。我自己曾将数值7.9转换后变成了 7.8999956589965... - Chris
2
Apache POI javadocs非常清楚,你不应该这样做如果你想要获取数字单元格的字符串值,请停止!这不是正确的方法。相反,为了获取数字、布尔或日期单元格的字符串值,请使用DataFormatter。 - Gagravarr

5

尝试:

new java.text.DecimalFormat("0").format( cell.getNumericCellValue() )

应该正确格式化数字。

据我理解,提问者想要区分22.0。您的解决方案无法实现这一点。(但是,欢迎来到Stack Overflow!) - Paŭlo Ebermann

2
你可以使用Java将数字单元格读取为字符串。
int type = cell.getCellType();
if(type == 0){
   String value = NumberToTextConverter.toText(cell.getNumericCellValue());
}
else{
   value = String.valueOf(cell.getStringCellValue());
}

这里,
0 => 数字单元格
getCellType() => 此方法用于获取Excel单元格的类型。

1
只要用户在输入数字之前将单元格设置为文本格式,POI 就允许您将其作为字符串获取值。其中一个关键是,如果格式为文本的单元格的左上角有一个小绿色三角形,您就可以将其作为字符串检索其值(每当将看起来像数字的内容强制转换为文本格式时,绿色三角形就会出现)。如果您有格式为文本的单元格包含数字,但 POI 不允许您将这些值作为字符串获取,那么您可以对电子表格数据进行一些操作以实现该目的:
  • 双击单元格,使编辑光标位于单元格内,然后单击“Enter”(每次只能处理一个单元格)。
  • 使用 Excel 2007 文本转换函数(可同时处理多个单元格)。
  • 将有问题的值剪切到另一个位置,将电子表格单元格重新格式化为文本,然后将先前剪切出的值作为未格式化值重新粘贴回正确的区域。

最后一件你可以做的事情是,如果你正在使用POI从Excel 2007电子表格中获取数据,你可以使用Cell类的'getRawValue()'方法。这不关心格式是什么。它将简单地返回一个带有原始数据的字符串。


0
public class Excellib {
public String getExceldata(String sheetname,int rownum,int cellnum, boolean isString) {
    String retVal=null;
    try {
        FileInputStream fis=new FileInputStream("E:\\Sample-Automation-Workspace\\SampleTestDataDriven\\Registration.xlsx");
        Workbook wb=WorkbookFactory.create(fis);
        Sheet s=wb.getSheet(sheetname);
        Row r=s.getRow(rownum);
        Cell c=r.getCell(cellnum);
        if(c.getCellType() == Cell.CELL_TYPE_STRING)
        retVal=c.getStringCellValue();
        else {
            retVal = String.valueOf(c.getNumericCellValue());
        }

我尝试了这个,对我有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接