2017-10-19 179 views
0

我需要将通过xls/xlsx上传的电话号码读取到Java String变量中,尽可能接近Excel文件中显示的内容。Apache POI无法检测到散列格式编号

所以我有这些资料填写: enter image description here

正如你可以看到,小区内的实际值是166609647,及其与60#############格式化,所以最后我们看到60166609647出现在细胞。

我想捕获字符串内容为60166609647在字符串,但到目前为止,我只能设法捕获166609647,任何人都可以启发我什么是错的?

注:如果我改变从60############的格式60000000000,我可以捕捉60166609647没有任何问题,但Excel是通过公共网站上传的,因此我不能强制执行。

的代码很简单,只要:

Cell cell = getTheCell(); // Got this after reading the sheets and rows 
DataFormatter df = new DataFormatter(); 
String value = df.formatCellValue(cell); 
// Here in value 
// If format is 600000000, I can get 60166609647 (right) 
// If format is 60#######, I get 166609647 (wrong) 

库我使用:

  • POI(POI)3.17
  • POI(POI-OOXML)3.17
  • POI( poi-ooxml-schemas)3.17
  • Java 7

有人知道我需要做什么才能把它弄清楚吗?

谢谢。

+0

外观像我的Apache POI错误。你有可能写一个简短的junit单元测试来展示这个问题,然后在Apache POI bugzilla中打开一个bug。 – Gagravarr

+0

@Gagravarr我已经在https://bz.apache.org/bugzilla/show_bug.cgi?id=61638提交了一个请求,有什么需要修改的吗? –

回答

1

问题是多维的。

首先,数字格式60############不能用于应用Java。它导致java.lang.IllegalArgumentException: Malformed pattern "60############"使用DecimalFormat

但是,如果需要的具有“60”为前缀的每个号码,则Excel数字格式\6\0#"60"#应该是可能的,并且应当被翻译成的DecimalFormat图案'60'#。但apache poiDataFormatter没有,因为它只是从Excel的格式字符串中删除所有引用,这导致60#也是格式不正确。

问题出在DataFormatter.java:671ff

我已经在我的MyDataFormatter修补了这个像这样:

... 
     // Now, handle the other aspects like 
     // quoting and scientific notation 
     for(int i = 0; i < sb.length(); i++) { 
      char c = sb.charAt(i); 
/* 
      // remove quotes and back slashes 
      if (c == '\\' || c == '"') { 
       sb.deleteCharAt(i); 
       i--; 
*/ 
      // handle quotes and back slashes 
      if (c == '\\') { 
       sb.setCharAt(i, '\''); 
       sb.insert(i+2, '\''); 
       i+=2; 
      } else if (c == '"') { 
       sb.setCharAt(i, '\''); 
      // for scientific/engineering notation 
      } else if (c == '+' && i > 0 && sb.charAt(i - 1) == 'E') { 
       sb.deleteCharAt(i); 
       i--; 
      } 
     } 

     formatStr = sb.toString(); 
     formatStr = formatStr.replace("''", ""); 
     return formatStr; 
    } 
... 

在这个例子中使用这样的:

import org.apache.poi.ss.usermodel.*; 
import org.apache.poi.ss.util.*; 

import java.io.FileInputStream; 

import java.lang.reflect.Method; 

class ExcelDataformatterExample { 

public static void main(String[] args) throws Exception { 

    Workbook wb = WorkbookFactory.create(new FileInputStream("ExcelExample.xlsx")); 

    DataFormatter df = new DataFormatter(); 
    MyDataFormatter mydf = new MyDataFormatter(); 

    Sheet sheet = wb.getSheetAt(0); 
    for (Row row : sheet) { 
    for (Cell cell : row) { 
    if (cell.getCellTypeEnum() == CellType.NUMERIC) { 
    CellReference cellRef = new CellReference(row.getRowNum(), cell.getColumnIndex()); 
    System.out.println("Cell " + cellRef.formatAsString()); 

    System.out.print("Excel's data format string: "); 
    String formatStr = cell.getCellStyle().getDataFormatString(); 
    System.out.println(formatStr); 

    System.out.print("Value using poi's data formatter: "); 
    Method cleanFormatForNumber = DataFormatter.class.getDeclaredMethod("cleanFormatForNumber", String.class); 
    cleanFormatForNumber.setAccessible(true); 
    String cleanFormatStr = (String)cleanFormatForNumber.invoke(df, formatStr); 
    System.out.print("using poi's cleanFormatStr: "); 
    System.out.print(cleanFormatStr + " result: "); 
    String value = df.formatCellValue(cell); 
    System.out.println(value); 

    System.out.print("Value using my data formatter: "); 
    cleanFormatForNumber = MyDataFormatter.class.getDeclaredMethod("cleanFormatForNumber", String.class); 
    cleanFormatForNumber.setAccessible(true); 
    cleanFormatStr = (String)cleanFormatForNumber.invoke(mydf, formatStr); 
    System.out.print("using my cleanFormatStr: "); 
    System.out.print(cleanFormatStr + " result: "); 
    value = mydf.formatCellValue(cell); 
    System.out.println(value); 

    } 
    } 
    } 
    wb.close(); 

} 

} 

它导致下面的输出,如果值是在细胞A1A4格式化为Excel如图所示:

Cell A1 
Excel's data format string: \60########## 
Value using poi's data formatter: using poi's cleanFormatStr: 60########## result: 166609647 
Value using my data formatter: using my cleanFormatStr: '6'0########## result: 166609647 
Cell A2 
Excel's data format string: \60000000000 
Value using poi's data formatter: using poi's cleanFormatStr: 60000000000 result: 60166609647 
Value using my data formatter: using my cleanFormatStr: '6'0000000000 result: 60166609647 
Cell A3 
Excel's data format string: "60"# 
Value using poi's data formatter: using poi's cleanFormatStr: 60# result: 166609647 
Value using my data formatter: using my cleanFormatStr: '60'# result: 60166609647 
Cell A4 
Excel's data format string: \6\0# 
Value using poi's data formatter: using poi's cleanFormatStr: 60# result: 166609647 
Value using my data formatter: using my cleanFormatStr: '60'# result: 60166609647 
+0

非常感谢你为解决这个问题所做的努力。我试图消化代码,但根据结果,你是否暗示基本上它不是一个简单的任务来支持全球支持格式如“60 ####”?好消息是我实际上并不需要检测小数位和其他复杂的格式,所以如果我可以调用格式化的方法,我想我可以自己复制这些函数,只需要替换所有的' #'到'0',以得到我最终想要的? –

+0

@Chor Wai Chun:正如所说的,数字格式'60 ####'不能用Java的'DecimalFormat'应用。但这对我来说也没有意义。将所有数字前缀为“6”,并在数字长度最多为4位数字前加上0。这意味着什么?使用我描述的补丁可以将所有数字前缀为“60”的Excel格式为“60”#“或”\ 6 \ 0#“。 –

+0

它对世界其他地方实际上毫无意义,除了我们的国家代码是60的马来西亚以外,因此我们会将它附加在我们的电话号码前面以获得国际格式。我无法控制我的用户会采用什么样的格式,但我可以做的只是为他们的预测输入添加尽可能多的支持。所以我想在调用格式函数之前将其格式中的'#'替换为'0'是我所经历的最好的选择。 –