您现在的位置是:首页 > 文章详情

POI事件模式指北(二)-Excel2007

日期:2019-02-16点击:664

POI事件模式指北(二)-Excel2007

1、简介

Excel2007(.xlsx)是现在最常用的Excel格式,对这种文件的读取也是非常常见的需求;同样的POI也提供用户模式(User API) 和事件模式(Event API) 两种方式供大家使用。

POI的事件模式占用内存更小,它利用基础的XML数据进行处理,适用于愿意学习.xlsx文件结构以及在java中处理XML的开发人员。

那么事不宜迟,Let‘s go!

2、Excel文件的XML结构

了解文件结构之前先来看一下准备的文件,这个文件只有一个sheet页,结构也很简单。

1550396964486

Excel2007是用XMl格式储存,将要读取的文件后缀名改为.zip或者直接用解压缩工具打开,就可以看到这个Excel文件的结构了。

1550393152959

2.1、[Content_Types].xml

[Content_Types].xml文件描述了整个Excel文件的结构,也将根据这个文件组织后面的读取工作。

1550396370778

2.1、docProps文件夹

docProps文件夹下面有两个xml,里面记录的Excel的属性信息。

1550393197382

app.xml : 记录文档的创建时间和修改时间,以及标题、作者等。

core.xml: 记录文档的其他属性,包括版本、是否只读、安全等。

2.2、xl文件夹

xl文件夹包括了需要的数据和格式信息,是重点关注的对象。

1550394501999

workbook.xml: 记录了工作表基本信息,是我们重点关注的文件之一。

styles.xml: 记录了每个单元格的样式。

worksheets: 里面包括了我们的每个sheet的信息,储存在xml文件中。

2.3、workbook.xml

workbook.xml重点关注的就是sheets和sheet两个标签

  • sheet标签中name属性记录的就是sheet的名称
  • sheet标签中r:id属性记录了当前sheet和之前提到的记录sheet信息的xml之间的对应关系,储存在_rels文件夹下的xml文件中。
  • sheet标签还有一个属性state标识来是否隐藏。

1550396409429

2.4、worksheets文件夹

一般一个Excel文件有几个sheet页,就会有几个XML文件与之对应。其中sheet页和xml文件就是根据【新建 Microsoft Excel 工作表xl\_relsworkbook.xml.rels】文件对应起来的。

1550395764208

2.5、sheet1.xml

现在回头看一下开始时展示的文件,实在是再清晰不过了。row标签代表一行、c标签代表一个单元格、v标签代表单元格中的值;但是B2的值是0,可实际的值明明是a,这是怎么回事呢?

1550397233613

其实Excel将所有字符值都储存在共享字符集中,每个用到的地方只用对应的索引来表示,a是第一个字符值,所以索引是0。

2.6、小结

至此Excel文件的基本结构就介绍的差不多了,其实数据基本都储存在sheet*.xml中了,只需要按照规则读取出来就行了。

还有诸如公式单元格,合并单元格,隐藏的单元格并没有介绍,不过了解上面这些,即使遇到其他情况也能从容应对了。

3、读取.xlsx文件实例

读取Excel2007文件的思路就是,先解压,然后依次读取各个xml文件,然后按需要的方式处理。

  1. 用OPCPackage以压缩文件的形式打开文件。
  2. 读取文件流
  3. 捕获开始和结束标签并分类处理

下面是POI官网上的示例,做了简单的修改。

想要更完整的示例,可以看官方SVN上的例子

import java.io.InputStream; import java.util.Iterator; import org.apache.poi.ooxml.util.SAXHelper; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.xssf.eventusermodel.XSSFReader; import org.apache.poi.xssf.model.SharedStringsTable; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.ParserConfigurationException; public class ExampleEventUserModel { public void processOneSheet(String filename) throws Exception { OPCPackage pkg = OPCPackage.open(filename); XSSFReader r = new XSSFReader(pkg); SharedStringsTable sst = r.getSharedStringsTable(); XMLReader parser = fetchSheetParser(sst); //选择要处理的sheet页 //通常形式为rId#或rSheet#(这里选择了第一个) InputStream sheet1 = r.getSheet("rId1"); InputSource sheetSource = new InputSource(sheet1); parser.parse(sheetSource); sheet2.close(); } public void processAllSheets(String filename) throws Exception { OPCPackage pkg = OPCPackage.open(filename); XSSFReader r = new XSSFReader( pkg ); SharedStringsTable sst = r.getSharedStringsTable(); XMLReader parser = fetchSheetParser(sst); Iterator<InputStream> sheets = r.getSheetsData(); while(sheets.hasNext()) { System.out.println("处理新sheet:\n"); InputStream sheet = sheets.next(); InputSource sheetSource = new InputSource(sheet); parser.parse(sheetSource); sheet.close(); System.out.println(""); } } public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException, ParserConfigurationException { XMLReader parser = SAXHelper.newXMLReader(); ContentHandler handler = new SheetHandler(sst); parser.setContentHandler(handler); return parser; } /** * 请参阅 org.xml.sax.helpers.DefaultHandler javadocs */ private static class SheetHandler extends DefaultHandler { private SharedStringsTable sst; private String lastContents; private boolean nextIsString; private SheetHandler(SharedStringsTable sst) { this.sst = sst; } public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { // c代表单元格 if(name.equals("c")) { // 打印单元格 System.out.print(attributes.getValue("r") + " - "); // 确定该值是否为SST String cellType = attributes.getValue("t"); if(cellType != null && cellType.equals("s")) { nextIsString = true; } else { nextIsString = false; } } // 清除缓存 lastContents = ""; } public void endElement(String uri, String localName, String name) throws SAXException { // 获取对应的文本值 if(nextIsString) { int idx = Integer.parseInt(lastContents); lastContents = sst.getItemAt(idx).getString(); nextIsString = false; } // v代表单元格内容 // Output after we've seen the string contents if(name.equals("v")) { System.out.println(lastContents); } } public void characters(char[] ch, int start, int length) { lastContents += new String(ch, start, length); } } public static void main(String[] args) throws Exception { ExampleEventUserModel example = new ExampleEventUserModel(); example.processOneSheet(args[0]); example.processAllSheets(args[0]); } }

这个例子只处理了文本值,数字处理起来还要更容易一些,但是实际使用的时候会发现,因为xml文件中空单元格和空行直接跳过了,所以要考虑填补空白格和行的事情,只要熟悉了Excel的结构,还是不难的。

4、后记

现在微软终于开窍,使用xml格式组织Excel文件,让更方便的读取Excel文件成为可能。更贴近实战的示例我会在下一篇展示,敬请期待。


参考链接

Apache POI官网: https://poi.apache.org/index.html

原文链接:https://yq.aliyun.com/articles/690513
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章