【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

posted @ 2016-10-25 22:07  丶会飞的羊  阅读(431)  评论(0编辑  收藏  举报