POI事件模式指北(二)-Excel2007
POI事件模式指北(二)-Excel2007
1、简介
Excel2007(.xlsx)是现在最常用的Excel格式,对这种文件的读取也是非常常见的需求;同样的POI也提供用户模式(User API) 和事件模式(Event API) 两种方式供大家使用。
POI的事件模式占用内存更小,它利用基础的XML数据进行处理,适用于愿意学习.xlsx文件结构以及在java中处理XML的开发人员。
那么事不宜迟,Let‘s go!
2、Excel文件的XML结构
了解文件结构之前先来看一下准备的文件,这个文件只有一个sheet页,结构也很简单。
Excel2007是用XMl格式储存,将要读取的文件后缀名改为.zip或者直接用解压缩工具打开,就可以看到这个Excel文件的结构了。
2.1、[Content_Types].xml
[Content_Types].xml文件描述了整个Excel文件的结构,也将根据这个文件组织后面的读取工作。
2.1、docProps文件夹
docProps文件夹下面有两个xml,里面记录的Excel的属性信息。
app.xml : 记录文档的创建时间和修改时间,以及标题、作者等。
core.xml: 记录文档的其他属性,包括版本、是否只读、安全等。
2.2、xl文件夹
xl文件夹包括了需要的数据和格式信息,是重点关注的对象。
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标识来是否隐藏。
2.4、worksheets文件夹
一般一个Excel文件有几个sheet页,就会有几个XML文件与之对应。其中sheet页和xml文件就是根据【新建 Microsoft Excel 工作表xl\_relsworkbook.xml.rels】文件对应起来的。
2.5、sheet1.xml
现在回头看一下开始时展示的文件,实在是再清晰不过了。row标签代表一行、c标签代表一个单元格、v标签代表单元格中的值;但是B2的值是0,可实际的值明明是a,这是怎么回事呢?
其实Excel将所有字符值都储存在共享字符集中,每个用到的地方只用对应的索引来表示,a是第一个字符值,所以索引是0。
2.6、小结
至此Excel文件的基本结构就介绍的差不多了,其实数据基本都储存在sheet*.xml中了,只需要按照规则读取出来就行了。
还有诸如公式单元格,合并单元格,隐藏的单元格并没有介绍,不过了解上面这些,即使遇到其他情况也能从容应对了。
3、读取.xlsx文件实例
读取Excel2007文件的思路就是,先解压,然后依次读取各个xml文件,然后按需要的方式处理。
- 用OPCPackage以压缩文件的形式打开文件。
- 读取文件流
- 捕获开始和结束标签并分类处理
下面是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
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
清除过期日志的py脚本
本篇和大家分享的是一个清除过期日志的python脚本,年后第二篇希望对大家有帮助; 该python脚本创建的由来 代码及分析 crontab定时任务 该python脚本创建的由来 此由来,是在过年假期时突然被反馈告警服务器磁盘空间占用比例增大,当时通过df等命令定位到,是使用了某个开源任务调度框架日志增大并之前很多历史日志没有自动删除导致的; 因此,查看该框架的文档是否有自动清除配置,暂时没有找到自动清除日志的配置说明,于是乎浏览源码就是log4来记录的,本来打算扩展重写下log4让其具有自动清除日志的功能,但是想到以后可能还有其他项目的日志无法自动清除,于是乎有了本篇分享的python产出,仅仅配置下检测路径即可删除自定义n天之前的日志 代码及分析 先来上代码,具体如下: #! /usr/bin/python #coding=utf-8 imp
- 下一篇
基于django的视频点播网站开发-step6-个人中心功能
从本讲起,我们开始个人中心功能的开发。个人中心里面包括个人资料、修改密码、订阅设置、意见反馈这四部分。通过这部分的开发,我们将会接触到更多django的用法。 感兴趣的伙伴可通过网站演示预览网站,登录后点击右上角头像即可弹出个人中心相关菜单。 整体功能 个人中心模块是对用户的信息进行展示并可以编辑。其中个人资料、修改密码、订阅设置是对用户信息的编辑,反馈建议是属于创建新数据。 个人资料 这里主要是对个人资料进行编辑,先显示用户原有的信息,然后用户即可对其进行修改并保存,对于编辑功能,django有自己的解决方案,即通过通用视图类UpdateView对模型进行更改。关于Update的介绍,同学们可查阅官网介绍 因为前面已经建立过user模型,所以这里就不用再次建立了,我们直接使用之前的user模型即可。 需要我们做的就是在users/urls.py中添加个人资料的路由, path('profile/<int:pk>/', views.ProfileView.as_view(), name='profile'), 可以看到,这里我们需要传一个int参数做为主键,并传递给视图类P...
相关文章
文章评论
共有0条评论来说两句吧...