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

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

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

 //Accepts objects needed while parsing.
// @param styles Table of styles
// @param strings Table of shared strings
// @param cols Minimum number of columns to show
// @param target Sink for output
public MyXSSFSheetHandler(
StylesTable styles,
ReadOnlySharedStringsTable strings,
int cols,
PrintStream target) {
this.stylesTable = styles;
this.sharedStringsTable = strings;
this.minColumnCount = cols;
this.output = target;
this.value = new StringBuffer();
this.nextDataType = xssfDataType.NUMBER;
this.formatter = new DataFormatter();
rowlist = new ArrayList<String>(0);
rowReader = new RowReader();
rowMap = new HashMap<Integer, String>(0);
rowString = new StringBuffer();
}
// @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException
{ if ("inlineStr".equals(name) || "v".equals(name))
{
vIsOpen = true;
// Clear contents cache
value.setLength(0);
}
// c => cell
else if ("c".equals(name))
{
// Get the cell reference
String r = attributes.getValue("r");
int firstDigit = -1;
for (int c = 0; c < r.length(); ++c)
{
if (Character.isDigit(r.charAt(c)))
{
firstDigit = c;
break;
}
}
thisColumn = nameToColumn(r.substring(0, firstDigit)); // Set up defaults.
this.nextDataType = xssfDataType.NUMBER;
this.formatIndex = -1;
this.formatString = null;
String cellType = attributes.getValue("t");
String cellStyleStr = attributes.getValue("s");
if ("b".equals(cellType))
nextDataType = xssfDataType.BOOL;
else if ("e".equals(cellType))
nextDataType = xssfDataType.ERROR;
else if ("inlineStr".equals(cellType))
nextDataType = xssfDataType.INLINESTR;
else if ("s".equals(cellType))
nextDataType = xssfDataType.SSTINDEX;
else if ("str".equals(cellType))
nextDataType = xssfDataType.FORMULA;
else if (cellStyleStr != null) {
// It's a number, but almost certainly one
// with a special style or format
int styleIndex = Integer.parseInt(cellStyleStr);
XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);
this.formatIndex = style.getDataFormat();
this.formatString = style.getDataFormatString();
if (this.formatString == null)
this.formatString = BuiltinFormats.getBuiltinFormat(this.formatIndex);
}
} } // @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
public void endElement(String uri, String localName, String name)
throws SAXException
{ String thisStr = null; // v => contents of a cell
if ("v".equals(name))
{
// Process the value contents as required.
// Do now, as characters() may be called more than once
switch (nextDataType) { case BOOL:
char first = value.charAt(0);
thisStr = first == '0' ? "FALSE" : "TRUE";
break; case ERROR:
thisStr = "\"ERROR:" + value.toString() + '"';
break; case FORMULA:
// A formula could result in a string value,
// so always add double-quote characters.
thisStr = '"' + value.toString() + '"';
break; case INLINESTR:
// TODO: have seen an example of this, so it's untested.
XSSFRichTextString rtsi = new XSSFRichTextString(value.toString());
thisStr = '"' + rtsi.toString() + '"';
break; case SSTINDEX:
String sstIndex = value.toString();
try {
int idx = Integer.parseInt(sstIndex);
XSSFRichTextString rtss = new XSSFRichTextString(sharedStringsTable.getEntryAt(idx));
thisStr = '"' + rtss.toString() + '"';
} catch (NumberFormatException ex) {
output.println("Failed to parse SST index '" + sstIndex + "': " + ex.toString());
}
break; case NUMBER:
String n = value.toString();
if (this.formatString != null)
thisStr = formatter.formatRawCellContents(Double.parseDouble(n), this.formatIndex, this.formatString);
else
thisStr = n;
break; default:
thisStr = "(TODO: Unexpected type: " + nextDataType + ")";
break;
} // Output after we've seen the string contents
// Emit commas for any fields that were missing on this row
if (lastColumnNumber == -1)
{
lastColumnNumber = 0;
}
for (int i = lastColumnNumber; i < thisColumn; ++i)
{
rowString.append(',');//每天加一个单元格的值到字符串中就追加一个逗号(末尾不添加)
//output.print(','); 可以看到使用output是可以将每一个单元格使用逗号分割
              //但是如果使用rowlist添加到列表中,却始终无法得到空单元格的内容
              //也就是说:空单元格被忽略了。
              //具体请参照标红的地方进行处理:使用字符串拼接的方式获得完整的行数据,再使用逗号拆分组合成rowMap
}
rowString.append(thisStr);// 这条code放在for后面,如果放在前面,会导致0和1两个单元格合为一个单元格。
// Might be the empty string.
//output.print(thisStr);
rowlist.add(thisStr);
// Update column
if (thisColumn > -1)
{
lastColumnNumber = thisColumn;
}
rowIndex++;
}
else if ("row".equals(name))
{ // Print out any missing commas if needed
if (minColumns > 0)
{
// Columns are 0 based
if (lastColumnNumber == -1)
{
lastColumnNumber = 0;
}
for (int i = lastColumnNumber; i < (this.minColumnCount); i++)
{
output.print(',');
}
} // We're onto a new row output.println();
output.println(countrows++);
lastColumnNumber = -1;
rowIndex = 0;
//rowMap = rowReader.getRowMap(rowlist);
rowMap = rowReader.getRowMapByString(rowString.toString());
// ADD =
rowLst1000.add(rowMap);
rowMap = null;
rowMap = new HashMap<Integer, String>(0);
if (countrows % 1000 == 0)
{
rowLst1000n.add(rowLst1000);
rowLst1000 = null;
rowLst1000 = new ArrayList<Map<Integer, String>>(0);
}
rowlist.clear();
System.out.println(rowString.toString());
rowString = null;
rowString = new StringBuffer();
}
}

以上是我自己的处理方式,当然还有其他的处理方式,再研究吧。毕竟写到此处的时候,我不过是一个不到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

【Java POI】POI基于事件驱动解析大数据量2007版本Excel,空值导致列错位问题的更多相关文章

  1. 基于EasyExcel的大数据量导入并去重

    源码:https://gitee.com/antia11/excel-data-import-demo 背景:客户需要每周会将上传一个 Excel 数据文件,数据量单次为 20W 以上,作为其他模块和 ...

  2. POI读写大数据量excel,解决超过几万行而导致内存溢出的问题

    1. Excel2003与Excel2007 两个版本的最大行数和列数不同,2003版最大行数是65536行,最大列数是256列,2007版及以后的版本最大行数是1048576行,最大列数是16384 ...

  3. POI读写大数据量EXCEL

    另一篇文章http://www.cnblogs.com/tootwo2/p/8120053.html里面有xml的一些解释. 大数据量的excel一般都是.xlsx格式的,网上使用POI读写的例子比较 ...

  4. [转]POI大数据量Excel解决方案

    全文转载自:jinshuaiwang的博客 目前处理Excel的开源javaAPI主要有两种,一是Jxl(Java Excel API),Jxl只支持Excel2003以下的版本.另外一种是Apach ...

  5. poi 操作Excel 以及大数据量导出

    maven 依赖 (版本必须一致,否则使用SXSSFworkbook 时程序会报错) <dependency> <groupId>org.apache.poi</grou ...

  6. java 导出Excel 大数据量,自己经验总结!

    出处: http://lyjilu.iteye.com/ 分析导出实现代码,XLSX支持: /** * 生成<span style="white-space: normal; back ...

  7. java excel大数据量导入导出与优化

    package com.hundsun.ta.utils; import java.io.File; import java.io.FileOutputStream; import java.io.I ...

  8. 大数据量查询优化——数据库设计、SQL语句、JAVA编码

    数据库设计方面: 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将 ...

  9. java大数据量调优

    从总体上来看,对于大型网站,比如门户网站,在面对大量用户访问.高并发请求方面,基本的解决方案集中在这样几个环节:1.首先需要解决网络带宽和Web请求的高并发,需要合理的加大服务器和带宽的投入,并且需要 ...

随机推荐

  1. 2.servlet的会话机制session

    session的说明: 1.session是服务端技术,存放在服务器 2.一个用户浏览器对应一个session域对象,一对一的对应关系 3.session的默认生命周期是30min,可以通过web.x ...

  2. isArray polyfill

    Array.isArray在ie9+浏览器上已经支持,可以放心使用.在垃圾浏览器上,可以说使用如下polyfill(出自MDN) if(!Array.isArray){ Array.isArray = ...

  3. rabbitmq的web管理界面无法使用guest用户登录

    安装最新版本的rabbitmq(3.3.1),并启用management plugin后,使用默认的账号guest登陆管理控制台,却提示登陆失败. 翻看官方的release文档后,得知由于账号gues ...

  4. echo '.SUFFIXES: .cpp' >> ${OUTPUT_FILE}

    当前makefile或shell内支持文件后缀的类型列表,意思是文件支持.cpp结尾的类型,并且将他,输出到OUTPUT_FILE函数. 见网上有人说: “makefile中 .SUFFIXES: . ...

  5. root用户自动登录

    编辑文件: /etc/gdm/custom.conf的内容: 1 # GDM configuration storage      2       3 [daemon]      4 #GtkModu ...

  6. 如何获取客户端MAC地址(三个方法)

    方法一: 调用Windows的DOS命令,从输出结果中读取MAC地址: public static String getMACAddress() { String address = "&q ...

  7. php开发中怎么获取服务端MAC地址?

    MAC(Media Access Control或者Medium Access Control)地址,意译为媒体访问控制,或称为物理地址.硬件地址,用来定义网络设备的位置.在php中如何获取MAC(M ...

  8. seajs的那点事(很坑的事),和本白的一点事(更坑的事)

    在开始之前,偶先吐槽加逗比一下,2天前,CCAV的本白和百度的菊花成功潜入到了携程大楼 然后在没有找到他们运维的情况下,四处乱逛,企图把他们的服务器给root一下,然后再瞎逛之后到了一个很神奇的地方 ...

  9. 绿书模拟day10 单词前缀

    [题目描述]一组单词是安全的,当且仅当不存在一个单词是另一个单词的前缀,这样才能保证数据不容易被误解,现在你手上有一个单词集合s,你需要计算有多少个自己是安全的.注意空集永远是安全的.[输入格式]第一 ...

  10. poj3335 半交平面,多边形内核

    Rotating Scoreboard Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 5300   Accepted: 21 ...