zoukankan      html  css  js  c++  java
  • 【Java POI】POI基于事件驱动解析大数据量2007版本Excel,空值导致列错位问题

    1.目前测试了20M的文件,可以读取。

    2.支持单个工作表1万+的数据行数,耗时如图。

    3.以下是关键地方处理的代码

      1 //Accepts objects needed while parsing.  
      2         // @param styles  Table of styles 
      3         // @param strings Table of shared strings 
      4         // @param cols    Minimum number of columns to show 
      5         // @param target  Sink for output  
      6         public MyXSSFSheetHandler(  
      7                 StylesTable styles,  
      8                 ReadOnlySharedStringsTable strings,  
      9                 int cols,  
     10                 PrintStream target) {  
     11             this.stylesTable = styles;  
     12             this.sharedStringsTable = strings;  
     13             this.minColumnCount = cols;  
     14             this.output = target;  
     15             this.value = new StringBuffer();  
     16             this.nextDataType = xssfDataType.NUMBER;  
     17             this.formatter = new DataFormatter();  
     18             rowlist = new ArrayList<String>(0);
     19             rowReader = new RowReader();
     20             rowMap = new HashMap<Integer, String>(0);
     21             rowString = new StringBuffer();
     22         }
     23         // @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
     24         public void startElement(String uri, String localName, String name,  
     25                                  Attributes attributes) throws SAXException 
     26         {  
     27   
     28             
     29             if ("inlineStr".equals(name) || "v".equals(name)) 
     30             {  
     31                 vIsOpen = true;  
     32                 // Clear contents cache  
     33                 value.setLength(0);  
     34             }  
     35             // c => cell  
     36             else if ("c".equals(name)) 
     37             {  
     38                 // Get the cell reference  
     39                 String r = attributes.getValue("r");  
     40                 int firstDigit = -1;  
     41                 for (int c = 0; c < r.length(); ++c) 
     42                 {  
     43                     if (Character.isDigit(r.charAt(c))) 
     44                     {  
     45                         firstDigit = c;  
     46                         break;  
     47                     }  
     48                 }  
     49                 thisColumn = nameToColumn(r.substring(0, firstDigit));  
     50   
     51                 // Set up defaults.  
     52                 this.nextDataType = xssfDataType.NUMBER;  
     53                 this.formatIndex = -1;  
     54                 this.formatString = null;  
     55                 String cellType = attributes.getValue("t");  
     56                 String cellStyleStr = attributes.getValue("s");  
     57                 if ("b".equals(cellType))  
     58                     nextDataType = xssfDataType.BOOL;  
     59                 else if ("e".equals(cellType))  
     60                     nextDataType = xssfDataType.ERROR;  
     61                 else if ("inlineStr".equals(cellType))  
     62                     nextDataType = xssfDataType.INLINESTR;  
     63                 else if ("s".equals(cellType))  
     64                     nextDataType = xssfDataType.SSTINDEX;  
     65                 else if ("str".equals(cellType))  
     66                     nextDataType = xssfDataType.FORMULA;  
     67                 else if (cellStyleStr != null) {  
     68                     // It's a number, but almost certainly one  
     69                     //  with a special style or format  
     70                     int styleIndex = Integer.parseInt(cellStyleStr);  
     71                     XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);  
     72                     this.formatIndex = style.getDataFormat();  
     73                     this.formatString = style.getDataFormatString();  
     74                     if (this.formatString == null)  
     75                         this.formatString = BuiltinFormats.getBuiltinFormat(this.formatIndex);  
     76                 }  
     77             }  
     78   
     79         }  
     80   
     81  
     82         // @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)       
     83         public void endElement(String uri, String localName, String name)  
     84                 throws SAXException 
     85         {  
     86   
     87             String thisStr = null;  
     88             
     89             // v => contents of a cell  
     90             if ("v".equals(name)) 
     91             {  
     92                 // Process the value contents as required.  
     93                 // Do now, as characters() may be called more than once  
     94                 switch (nextDataType) {  
     95   
     96                     case BOOL:  
     97                         char first = value.charAt(0);  
     98                         thisStr = first == '0' ? "FALSE" : "TRUE";  
     99                         break;  
    100   
    101                     case ERROR:  
    102                         thisStr = ""ERROR:" + value.toString() + '"';  
    103                         break;  
    104   
    105                     case FORMULA:  
    106                         // A formula could result in a string value,  
    107                         // so always add double-quote characters.  
    108                         thisStr = '"' + value.toString() + '"';  
    109                         break;  
    110   
    111                     case INLINESTR:  
    112                         // TODO: have seen an example of this, so it's untested.  
    113                         XSSFRichTextString rtsi = new XSSFRichTextString(value.toString());  
    114                         thisStr = '"' + rtsi.toString() + '"';  
    115                         break;  
    116   
    117                     case SSTINDEX:  
    118                         String sstIndex = value.toString();  
    119                         try {  
    120                             int idx = Integer.parseInt(sstIndex);  
    121                             XSSFRichTextString rtss = new XSSFRichTextString(sharedStringsTable.getEntryAt(idx));  
    122                             thisStr = '"' + rtss.toString() + '"';  
    123                         } catch (NumberFormatException ex) {  
    124                             output.println("Failed to parse SST index '" + sstIndex + "': " + ex.toString());  
    125                         }  
    126                         break;  
    127   
    128                     case NUMBER:  
    129                         String n = value.toString();  
    130                         if (this.formatString != null)  
    131                             thisStr = formatter.formatRawCellContents(Double.parseDouble(n), this.formatIndex, this.formatString);  
    132                         else  
    133                             thisStr = n;  
    134                         break;  
    135   
    136                     default:  
    137                         thisStr = "(TODO: Unexpected type: " + nextDataType + ")";  
    138                         break;  
    139                 }  
    140   
    141                 // Output after we've seen the string contents  
    142                 // Emit commas for any fields that were missing on this row  
    143                 if (lastColumnNumber == -1) 
    144                 {  
    145                     lastColumnNumber = 0;  
    146                 }  
    147                 for (int i = lastColumnNumber; i < thisColumn; ++i) 
    148                 { 
    149                     rowString.append(',');//每天加一个单元格的值到字符串中就追加一个逗号(末尾不添加)
    150                     //output.print(','); 可以看到使用output是可以将每一个单元格使用逗号分割
    151               //但是如果使用rowlist添加到列表中,却始终无法得到空单元格的内容
    152               //也就是说:空单元格被忽略了。
    153               //具体请参照标红的地方进行处理:使用字符串拼接的方式获得完整的行数据,再使用逗号拆分组合成rowMap
    154                 }
    155                 rowString.append(thisStr);// 这条code放在for后面,如果放在前面,会导致0和1两个单元格合为一个单元格。
    156                 // Might be the empty string.  
    157                 //output.print(thisStr);
    158                 rowlist.add(thisStr);
    159                 // Update column  
    160                 if (thisColumn > -1)
    161                 {
    162                     lastColumnNumber = thisColumn;
    163                 }
    164                 rowIndex++;
    165             } 
    166             else if ("row".equals(name)) 
    167             {  
    168   
    169                 // Print out any missing commas if needed  
    170                 if (minColumns > 0) 
    171                 {  
    172                     // Columns are 0 based  
    173                     if (lastColumnNumber == -1) 
    174                     {  
    175                         lastColumnNumber = 0;  
    176                     }  
    177                     for (int i = lastColumnNumber; i < (this.minColumnCount); i++) 
    178                     {  
    179                         output.print(',');
    180                     }  
    181                 }  
    182   
    183                 // We're onto a new row  
    184   
    185                 output.println();  
    186                 output.println(countrows++);  
    187                 lastColumnNumber = -1;   
    188                 rowIndex = 0;
    189                 //rowMap = rowReader.getRowMap(rowlist);
    190                 rowMap = rowReader.getRowMapByString(rowString.toString());
    191                 // ADD =
    192                 rowLst1000.add(rowMap);
    193                 rowMap = null;
    194                 rowMap = new HashMap<Integer, String>(0);
    195                 if (countrows % 1000 == 0)
    196                 {
    197                     rowLst1000n.add(rowLst1000);
    198                     rowLst1000 = null;
    199                     rowLst1000 = new ArrayList<Map<Integer, String>>(0);
    200                 }
    201                 rowlist.clear();
    202                 System.out.println(rowString.toString());
    203                 rowString = null;
    204                 rowString = new StringBuffer();
    205             }  
    206         }
    View Code

    以上是我自己的处理方式,当然还有其他的处理方式,再研究吧。毕竟写到此处的时候,我不过是一个不到1年经验的小菜鸟。

    补充:

      上面的处理方式不够明智,如果单元格中的文本本生就带有逗号,那么会导致分割错误。

      建议:

    143          if (lastColumnNumber == -1) 
    144                 {  
    145                     lastColumnNumber = 0;  
                  // 此处使用list或者map进行存储
                  list.add(null);//添加一个空值 146 } 147 for (int i = lastColumnNumber; i < thisColumn; ++i)
                此处改为:
                for (int i = lastColumnNumber + 1; i < thisColumn; ++i) // lastColumnNumber + 1 确保不会因为连续两个空单元格而出错。
    148           { 
    149             //rowString.append(',');//每天加一个单元格的值到字符串中就追加一个逗号(末尾不添加) 150 //output.print(','); 可以看到使用output是可以将每一个单元格使用逗号分割
                  //但是如果使用rowlist添加到列表中,却始终无法得到空单元格的内容
                  //也就是说:空单元格被忽略了。
                  //具体请参照标红的地方进行处理:使用字符串拼接的方式获得完整的行数据,再使用逗号拆分组合成rowMap
                  
                  // 此处使用list或者map进行存储
                  list.add(null);//添加一个空值
    151                 }
                // if 和 for 之后再添加当前单元格字符串
                list.add(thisStr);

    记住:可以打断点自己跑一跑,不难发现,for循环中所追加的逗号是当前单元格添加的,所以并不是说第一个格之后和第二个单元格之前刻意添加的。明白这个if和for的具体作用后,就能顺利的为空单元格赋值,且绕开使用字符串拼接导致的潜在问题。

    下面附上其余代码的参照地址:

    java使用POI通过事件模型解析超过40M的Excel文件,解决空单元格问题

    http://www.360sdn.com/java/2014/0524/3392.html

  • 相关阅读:
    Inter IPP 跟 Microsoft V100编译器区别
    目标描述(基于区域的描述)
    目标描述(基于边界的描述)
    目标表达(基于区域的表达)
    表达描述(基于变换的表达)
    目标表达(基于边界的表达)
    分割技术扩展
    分水岭分割算法
    借助均移确定聚类中心
    借助过度区选择阈值
  • 原文地址:https://www.cnblogs.com/flydkPocketMagic/p/5998482.html
Copyright © 2011-2022 走看看