使用Apache-POI将Excel十进制格式解析为Java BigDecimal

时间:2018-02-09 08:33:01

标签: java excel apache-poi

我想借助Apache-POI(XSSF和SAX事件API)导入XLSX文件。

因为Excel将数字存储为浮点数,所以在java中必须将它们格式化回原来在Excel中查找的方式。这可以通过读取单元格格式来实现:

String cellStyle = sheetReader.getAttributeValue(null, "s");
if (cellStyle != null) {
  // save the format of the cell for later use.
  int styleIndex = Integer.parseInt(cellStyle);
  XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);
  formatIndex = style.getDataFormat();
  formatString = style.getDataFormatString();
  if (formatString == null) {
    // formatString could not be found, so it must be a builtin format.
    formatString = BuiltinFormats.getBuiltinFormat(formatIndex);
  }
}
...
// format the floating-point value
String xlsxValue = formatter.formatRawCellContents(
  Double.parseDouble(value),
  formatIndex,
  formatString);

上面的代码对我来说效果很好......但它给我的数字就像在德国Locale中运行Excel时最初在Excel中格式化的那样。这些数字的示例:

10,30
100.00.00,43

现在我如何重新格式化这些数字,以便将它们提供给Java Double和Java BigDecimal?

Apache-POI似乎没有为这种情况提供实用程序类,但是如何在java中处理这些数字呢?

我已经入侵了poi以使这种情况发生,但是没有别的办法吗?

// hack apache-poi classes that are private, so we can retrieve the 'format'
// which helps us to transform the formated value to the expected java-format
CellStyle style = new CellStyleHack(formatIndex, formatString);
Cell cell = new CellHack(Double.parseDouble(xlsxValue), style);

java.text.Format format = formatter.createFormat(cell);
if (format instanceof DecimalFormat) {
  DecimalFormat decimalFormat = ((DecimalFormat) format);
  char dSep = decimalFormat.getDecimalFormatSymbols().getDecimalSeparator();
  char gSep = decimalFormat.getDecimalFormatSymbols().getGroupingSeparator();
  String cSymbol = decimalFormat.getDecimalFormatSymbols().getCurrencySymbol();

  // java always expects '.' as decimal seperator for BigDecimal and Double.
  xlsxValue = xlsxValue.replace("" + gSep, "");
  xlsxValue = xlsxValue.replace(dSep, '.');
  if (cSymbol != null) {
    xlsxValue = xlsxValue.replace(cSymbol, "").trim();
  }
}

2 个答案:

答案 0 :(得分:1)

来自Apache POI docs

Cell.getNumericCellValue()应该已经返回一个double值。

对于其他格式,请使用 DataFormatter 类:

  

DataFormatter包含格式化存储在的值的方法   细胞。这对于您的报表和GUI演示非常有用   需要显示与Excel中显示的数据完全相同的数据。支持的格式   包括货币,SSN,百分比,小数,日期,电话号码,   邮政编码等。

使用XSSF SAX事件API时,您没有那种访问权限,但幸运的是有一个代码示例

https://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/xssf/eventusermodel/XLSX2CSV.java

显示如何通过实现 SheetContentsHandler 接口并覆盖其单元 startRow 来检索单元格的数字/格式化字符串值, endRow 等方法(在示例中,查找 XLSX2CSV.SheetToCSV.cell (...)方法。

希望这有帮助。

答案 1 :(得分:1)

在@AxelRichter的帮助下,以下解决方案现在解决了我的问题:

// we must use Locale.US, because we want to make sure that the DataFormatter will
// always product "." as decimal-separator and "," as thousands-separator.
this.formatter = new DataFormatter(Locale.US);

// format the floating-point value
String xlsxValue = formatter.formatRawCellContents(
        Double.parseDouble(value),
        formatIndex,
        formatString);

// xlsxValue may contain format-symbols, which we need to remove...
xlsxValue = xlsxValue.replaceAll("[^\\d.]", "");