我目前正在写我的学士论文,并且正在使用来自Apache的POI Event API。简而言之,我的工作是关于从Excel读取数据的更有效方法。
开发人员一再问我,事件API到底是什么意思。不幸的是,我在Apache页面上找不到关于基本原理的任何信息。
以下代码,我如何使用POI事件API(这来自XSSF和SAX的Apache示例):
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);
// To look up the Sheet Name / Sheet Order / rID,
// you need to process the core Workbook stream.
// Normally it's of the form rId# or rSheet#
InputStream sheet2 = r.getSheet("rId2");
InputSource sheetSource = new InputSource(sheet2);
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("Processing new 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;
}
/**
* See 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 => cell
if(name.equals("c")) {
// Print the cell reference
System.out.print(attributes.getValue("r") + " - ");
// 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;
}
}
// Clear contents cache
lastContents = "";
}
public void endElement(String uri, String localName, String name)
throws SAXException {
// Process the last contents as required.
// Do now, as characters() may be called more than once
if(nextIsString) {
int idx = Integer.parseInt(lastContents);
lastContents = sst.getItemAt(idx).getString();
nextIsString = false;
}
// v => contents of a cell
// 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]);
}
}
有人可以向我解释Event API的工作原理吗?它与基于事件的体系结构相同还是其他?
答案 0 :(得分:2)
*.xlsx
文件(存储在Office Open XML中,Excel
作为apache poi
处理的文件)是包含数据的XSSF
存档在目录结构中的ZIP
个文件中。因此,我们可以解压缩XML
文件,然后直接从*.xlsx
文件中获取数据。
其中有XML
,其中包含所有字符串单元格值。并且/xl/sharedStrings.xml
描述了工作簿的结构。并且/xl/workbook.xml
正在存储工作表的数据。并且/xl/worksheets/sheet1.xml, /xl/worksheets/sheet2.xml, ...
具有工作表中所有单元格的样式设置。
默认情况下,在创建/xl/styles.xml
文件时,XSSFWorkbook
文件的所有那些部分将成为对象表示形式,分别为*.xlsx
,XSSFWorkbook
,XSSFSheet
,{{1 }},...以及内存中XSSFRow
的其他对象。
要了解一下XSSFCell
,org.apache.poi.xssf.*.*
和XSSFSheet
的内存消耗情况,可以查看源代码。这些对象中的每一个都包含多个XSSFRow
和XSSFCell
作为内部成员,当然也包含多个方法。现在,想象一张有成千上万行的工作表,每行包含多达数百个单元格。这些行和单元格中的每一个将由内存中的List
或Map
表示。这不能成为XSSFRow
的指责,因为如果需要使用这些对象,则这些对象是必需的。但是,如果实际上只需要从XSSFCell
表中获取内容,则这些对象并不是全部必需的。这就是XSSF and SAX (Event API)方法的原因。
因此,如果只需要从工作表中读取数据,则可以简单地解析所有apache poi
文件中的Excel
,而无需为每张工作表,每一行和每一行创建消耗内存的对象这些表中的单元格。
在基于事件的模式下解析XML
意味着该代码自上而下遍历/xl/worksheets/sheet[n].xml
并定义了回调方法,如果代码检测到元素的开始,元素的结束,则调用该方法或元素中的字符内容。然后,适当的回调方法处理在元素的开始,结束或使用字符内容时要做什么。因此,读取XML
文件仅意味着一次自上而下地浏览文件,处理事件(元素的开始,结束,字符内容)并能够从中获取所有需要的内容。因此,将内存消耗减少为存储从XML
获得的文本数据。
XSSF and SAX (Event API)使用类XML
为此扩展了DefaultHandler。
但是,如果我们已经处于获取基础XML
数据并进行处理的这一级别,那么我们也可以再退一步。本机SheetHandler
能够处理XML
并解析Java
。因此,我们甚至根本不需要其他库。请参阅how read excel file having more than 100000 row in java?,其中已显示了此内容。我的代码使用Package javax.xml.stream,该代码还提供基于事件的ZIP
,但不使用回调,而是使用线性代码。也许这段代码更容易理解,因为它是一个整体。
为检测数字格式是否为日期格式,因此格式化的单元格包含日期/时间值,使用了一个单独的XML
类XMLEventReader
。这样做是为了简化代码。当然,即使是此类,我们也可以编写自己的代码。