POI Sax 事件驱动解析Excel2007文件
ccwgpt 2024-12-17 13:00 95 浏览 0 评论
Excel2007版本的代码如下,本文主要是用于POI解析大文件Excel容易出现内存溢出的现象而提出解决方案,故此解决了大数据量的Excel文件解析的难度,在此拿出来贡献给大家,谢谢!
1. Office2007与Office Open XML
在Office 2007之前,Office一直都是以二进制位的方式存储,但这种格式不易被其它软件拿来使用,在各界的压力下,MicroSoft于2005年发布了基于XML的ooxml开放文档标准。ooxml的xml schema强调减少load time,增快parsing speed,将child elements分开存储,而不是multiple attributes一起存,这有点类似于HTML的结构。ooxml 使用XML和ZIP技术结合进行文件存储,因为XML是一个基于文本的格式,而且ZIP容器支持内容的压缩,所以其一大优势就是可以大大减小文件的尺寸。其它特点这里不再叙述。
2. SAX方式解析XML
SAX全称Simple API for XML,它是一个接口,也是一个软件包。它是一种XML解析的替代方法,不同于DOM解析XML文档时把所有内容一次性加载到内存中的方式,它逐行扫描文档,一边扫描,一边解析。所以那些只需要单遍读取内容的应用程序就可以从SAX解析中受益,这对大型文档的解析是个巨大优势。另外,SAX “推" 模型可用于广播环境,能够同时注册多个ContentHandler,并行接收事件,而不是在一个管道中一个接一个地进行处理。一些支持 SAX 的语法分析器包括 Xerces,Apache parser(以前的 IBM 语法分析器)、MSXML(Microsoft 语法分析器)和 XDK(Oracle 语法分析器)。这些语法分析器是最灵活的,因为它们还支持 DOM。
3. POI以SAX解析excel2007文件
所需jar包:poi-3.10-FINAL-20140208.jar,poi-ooxml-3.10-FINAL-20140208.jar, poi-ooxml-schemas-3.10-FINAL-20140208.jar
xercesImpl.jar xml-apis-2.0.2.jar xmlbeans-2.6.0.jar sax2.jar
package com.boguan.bte.util.excel; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.ss.usermodel.BuiltinFormats; import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.xssf.eventusermodel.XSSFReader; import org.apache.poi.xssf.model.SharedStringsTable; import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.usermodel.XSSFCellStyle; import org.apache.poi.xssf.usermodel.XSSFRichTextString; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; import com.boguan.bte.service.common.IExcelRowReader; import com.boguan.bte.service.common.impl.ExcelRowReader; /** * 名称: ExcelXlsxReader.java<br> * 描述: <br> * 类型: JAVA<br> * 最近修改时间:2016年7月5日 上午10:00:52<br> * * @since 2016年7月5日 * @author “” */ public class ExcelXlsxReader extends DefaultHandler { private IExcelRowReader rowReader; public void setRowReader(IExcelRowReader rowReader) { this.rowReader = rowReader; } /** * 共享字符串表 */ private SharedStringsTable sst; /** * 上一次的内容 */ private String lastContents; /** * 字符串标识 */ private boolean nextIsString; /** * 工作表索引 */ private int sheetIndex = -1; /** * 行集合 */ private List<String> rowlist = new ArrayList<String>; /** * 当前行 */ private int curRow = 0; /** * 当前列 */ private int curCol = 0; /** * T元素标识 */ private boolean isTElement; /** * 异常信息,如果为空则表示没有异常 */ private String exceptionMessage; /** * 单元格数据类型,默认为字符串类型 */ private CellDataType nextDataType = CellDataType.SSTINDEX; private final DataFormatter formatter = new DataFormatter; private short formatIndex; private String formatString; // 定义前一个元素和当前元素的位置,用来计算其中空的单元格数量,如A6和A8等 private String preRef = null, ref = null; // 定义该文档一行最大的单元格数,用来补全一行最后可能缺失的单元格 private String maxRef = null; /** * 单元格 */ private StylesTable stylesTable; /** * 遍历工作簿中所有的电子表格 * * @param filename * @throws IOException * @throws OpenXML4JException * @throws SAXException * @throws Exception */ public void process(String filename) throws IOException, OpenXML4JException, SAXException { OPCPackage pkg = OPCPackage.open(filename); XSSFReader xssfReader = new XSSFReader(pkg); stylesTable = xssfReader.getStylesTable; SharedStringsTable sst = xssfReader.getSharedStringsTable; XMLReader parser = this.fetchSheetParser(sst); Iterator<InputStream> sheets = xssfReader.getSheetsData; while (sheets.hasNext) { curRow = 0; sheetIndex++; InputStream sheet = sheets.next; InputSource sheetSource = new InputSource(sheet); parser.parse(sheetSource); sheet.close; } } public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException { XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser"); this.sst = sst; parser.setContentHandler(this); return parser; } public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { // c => 单元格 if ("c".equals(name)) { // 前一个单元格的位置 if (preRef == null) { preRef = attributes.getValue("r"); } else { preRef = ref; } // 当前单元格的位置 ref = attributes.getValue("r"); // 设定单元格类型 this.setNextDataType(attributes); // Figure out if the value is an index in the SST String cellType = attributes.getValue("t"); if (cellType != null && cellType.equals("s")) { nextIsString = true; } else { nextIsString = false; } } // 当元素为t时 if ("t".equals(name)) { isTElement = true; } else { isTElement = false; } // 置空 lastContents = ""; } /** * 单元格中的数据可能的数据类型 */ enum CellDataType { BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER, DATE, NULL } /** * 处理数据类型 * * @param attributes */ public void setNextDataType(Attributes attributes) { nextDataType = CellDataType.NUMBER; formatIndex = -1; formatString = null; String cellType = attributes.getValue("t"); String cellStyleStr = attributes.getValue("s"); String columData = attributes.getValue("r"); if ("b".equals(cellType)) { nextDataType = CellDataType.BOOL; } else if ("e".equals(cellType)) { nextDataType = CellDataType.ERROR; } else if ("inlineStr".equals(cellType)) { nextDataType = CellDataType.INLINESTR; } else if ("s".equals(cellType)) { nextDataType = CellDataType.SSTINDEX; } else if ("str".equals(cellType)) { nextDataType = CellDataType.FORMULA; } if (cellStyleStr != null) { int styleIndex = Integer.parseInt(cellStyleStr); XSSFCellStyle style = stylesTable.getStyleAt(styleIndex); formatIndex = style.getDataFormat; formatString = style.getDataFormatString; if ("m/d/yy" == formatString) { nextDataType = CellDataType.DATE; formatString = "yyyy-MM-dd hh:mm:ss.SSS"; } if (formatString == null) { nextDataType = CellDataType.NULL; formatString = BuiltinFormats.getBuiltinFormat(formatIndex); } } } /** * 对解析出来的数据进行类型处理 * * @param value * 单元格的值(这时候是一串数字) * @param thisStr * 一个空字符串 * @return */ @SuppressWarnings("deprecation") public String getDataValue(String value, String thisStr) { switch (nextDataType) { // 这几个的顺序不能随便交换,交换了很可能会导致数据错误 case BOOL: char first = value.charAt(0); thisStr = first == '0' ? "FALSE" : "TRUE"; break; case ERROR: thisStr = "\"ERROR:" + value.toString + '"'; break; case FORMULA: thisStr = '"' + value.toString + '"'; break; case INLINESTR: XSSFRichTextString rtsi = new XSSFRichTextString(value.toString); thisStr = rtsi.toString; rtsi = null; break; case SSTINDEX: String sstIndex = value.toString; try { int idx = Integer.parseInt(sstIndex); XSSFRichTextString rtss = new XSSFRichTextString(sst.getEntryAt(idx)); thisStr = rtss.toString; rtss = null; } catch (NumberFormatException ex) { thisStr = value.toString; } break; case NUMBER: if (formatString != null) { thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString).trim; } else { thisStr = value; } thisStr = thisStr.replace("_", "").trim; break; case DATE: thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString); // 对日期字符串作特殊处理 thisStr = thisStr.replace(" ", "T"); break; default: thisStr = " "; break; } return thisStr; } @Override public void endElement(String uri, String localName, String name) throws SAXException { // 根据SST的索引值的到单元格的真正要存储的字符串 // 这时characters方法可能会被调用多次 if (nextIsString) { int idx = Integer.parseInt(lastContents); lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString; } // t元素也包含字符串 if (isTElement) { // 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符 String value = lastContents.trim; rowlist.add(curCol, value); curCol++; isTElement = false; } else if ("v".equals(name)) { // v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引 String value = this.getDataValue(lastContents.trim, ""); // 补全单元格之间的空单元格 if (!ref.equals(preRef)) { int len = countNullCell(ref, preRef); for (int i = 0; i < len; i++) { rowlist.add(curCol, ""); curCol++; } } rowlist.add(curCol, value); curCol++; } else { // 如果标签名称为 row ,这说明已到行尾,调用 optRows 方法 if (name.equals("row")) { // 默认第一行为表头,以该行单元格数目为最大数目 if (curRow == 0) { maxRef = ref; } // 补全一行尾部可能缺失的单元格 if (maxRef != null) { int len = countNullCell(maxRef, ref); for (int i = 0; i <= len; i++) { rowlist.add(curCol, ""); curCol++; } } rowReader.getRows(sheetIndex, curRow, rowlist); rowlist.clear; curRow++; curCol = 0; preRef = null; ref = null; } } } /** * 计算两个单元格之间的单元格数目(同一行) * * @param ref * @param preRef * @return */ public int countNullCell(String ref, String preRef) { // excel2007最大行数是1048576,最大列数是16384,最后一列列名是XFD String xfd = ref.replaceAll("\\d+", ""); String xfd_1 = preRef.replaceAll("\\d+", ""); xfd = fillChar(xfd, 3, '@', true); xfd_1 = fillChar(xfd_1, 3, '@', true); char letter = xfd.toCharArray; char letter_1 = xfd_1.toCharArray; int res = (letter[0] - letter_1[0]) * 26 * 26 + (letter[1] - letter_1[1]) * 26 + (letter[2] - letter_1[2]); return res - 1; } /** * 字符串的填充 * * @param str * @param len * @param let * @param isPre * @return */ String fillChar(String str, int len, char let, boolean isPre) { int len_1 = str.length; if (len_1 < len) { if (isPre) { for (int i = 0; i < (len - len_1); i++) { str = let + str; } } else { for (int i = 0; i < (len - len_1); i++) { str = str + let; } } } return str; } @Override public void characters(char[] ch, int start, int length) throws SAXException { // 得到单元格内容的值 lastContents += new String(ch, start, length); } /** * @return the exceptionMessage */ public String getExceptionMessage { return exceptionMessage; } public static void main(String[] args) { IExcelRowReader rowReader = new ExcelRowReader; try { // ExcelReaderUtil.readExcel(rowReader, // "E://2016-07-04-011940a.xls"); System.out.println("**********************************************"); ExcelReaderUtil.readExcel(rowReader, "E://test.xlsx"); } catch (Exception e) { e.printStackTrace; } } }
相关推荐
- 丨公司丨公司大架构整理汇总
-
注:本文转自团队成员原创作品,特此鸣谢(公号:法海图鉴)今日话题公司大架构整理背景介绍经过前几期话题对各种企业类型的介绍,想必大家已经有了初步认识。之后我将带着大家开启对公司的深入了解。本期...
- 图解物理--八年级物理下册最全知识框架导图
-
第七章力1力2弹力3重力第八章运动和力1牛顿第一定律2二力平衡3摩擦力第九章压强1压强2液体压强3大气压强4流体压强与流速的关系第十章浮力1浮力2阿基米德原理3物体的浮沉条件及应用第十一章功...
- 八年级上册生物,思维导图,期末高分必备资料,家长收藏
-
这是八年级上册生物的思维导图,孩子在背诵知识点的时候,可以看一下知识点在导图中的位置,形成对知识点整体的把握,有助于学生拿高分,特别是图片中带红色星星的部分,更是要注意背诵,是重点内容。家长可以把图片...
- 2019政府工作报告精华,这张思维导图里全都有
-
每经记者:李可愚每经编辑:陈星每日经济新闻
- 图解薪酬体系结构设计
-
...
- 司考复习独家总结!一张图总结行政法知识结构体系
-
作为三大实体法之一,行政法的分值在60分左右,行政法在司法考试中一直比较平稳常规,没有偏题怪题,还是比较容易得分的。小编要提醒大家,在3月之前要把三大实体法学习一遍。下图是厚大在线360导学师小周总结...
- 实用干货!高中物理框架图,让零碎知识“串联”起来
-
高中物理学习一定要抓好逻辑结构大框架!了解整个知识框架体系后,更易抓住骨干知识,干掉重难知识点~今天给大家分享高中物理的框架图同学们赶紧收藏起来吧!力学知识结构图光学知识结构图热学、原子物理知识结构图...
- 254m超高层办公楼型钢砼框架-核心筒结构图
-
高度类别:超高层建筑钢筋混凝土结构:框架,框架核心筒钢结构:钢框架建筑功能:办公包含:办公楼57层(-3层)254.150m钻孔灌注桩桩+筏板型钢混凝土框架-钢筋混凝土核心筒西裙房2层(-...
- 砖混结构与框架结构,究竟有何区别?千万别被坑!
-
现在买房装修的人最怕啥?不是价格高,而是房子不安全!两种主流建筑结构,砖混靠墙,框架靠柱子,选错了隔墙都可能要命。简单说,砖混便宜但别碰高层,框架贵点但能保命。砖混那些承重墙根本不能拆,想砸墙改个开放...
- 大师一百——高中化学必考:《元素周期律》考点框架图
-
今天大师给大家带来的是高中化学的《元素周期律》考点框架图,高中的同学必须牢记于心,这种重要的考点,考试是一定会考的!化学大师...
- 需求分析框架图
-
需求分析框架图
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 框架图 (58)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- jpa框架 (47)
- springmvc框架 (49)
- 分布式事务框架 (65)
- scrapy框架 (56)
- shiro框架 (61)
- 定时任务框架 (56)
- java日志框架 (61)
- JAVA集合框架 (47)
- mfc框架 (52)
- abb框架断路器 (48)
- beego框架 (52)
- java框架spring (58)
- grpc框架 (65)
- tornado框架 (48)
- 前端框架bootstrap (54)
- orm框架有哪些 (51)
- ppt框架 (48)
- 内联框架 (52)
- cad怎么画框架 (58)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)