POI以SAX方式解析Excel2007大文件(包含空单元格的处理)

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

   辅助工具:Open XML SDK 2.5

   原始文件:book1.xlsx

   POI以SAX方式解析Excel2007大文件(包含空单元格的处理)_第1张图片

   SDK展示:注意其中第四行只有三个cell元素,第五行只有两个cell元素,而这种空单元格的处理正是我们所要注意的

POI以SAX方式解析Excel2007大文件(包含空单元格的处理)_第2张图片

   程序源码:

  

package test;

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

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.ContentHandler;
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;

public class ExampleEventUserModel {
	
	private static StylesTable stylesTable;
	
	/**
	 * 处理一个sheet
	 * @param filename
	 * @throws Exception
	 */
	public void processOneSheet(String filename) throws Exception {
		
		OPCPackage pkg = OPCPackage.open(filename);
		XSSFReader r = new XSSFReader( pkg );
		stylesTable = r.getStylesTable(); 
		SharedStringsTable sst = r.getSharedStringsTable();

		XMLReader parser = fetchSheetParser(sst);

		// Seems to either be rId# or rSheet#
		InputStream sheet2 = r.getSheet("rId1");
		InputSource sheetSource = new InputSource(sheet2);
		parser.parse(sheetSource);
		sheet2.close();
	}

	/**
	 * 处理所有sheet
	 * @param filename
	 * @throws Exception
	 */
	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 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("");
		}
	}

	/**
	 * 获取解析器
	 * @param sst
	 * @return
	 * @throws SAXException
	 */
	public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {
		XMLReader parser =
			XMLReaderFactory.createXMLReader(
					"org.apache.xerces.parsers.SAXParser"
			);
		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 List rowlist = new ArrayList(); 
	    private int curRow = 0; 
	    private int curCol = 0;
	    
	    //定义前一个元素和当前元素的位置,用来计算其中空的单元格数量,如A6和A8等
	    private String preRef = null, ref = null;
	    //定义该文档一行最大的单元格数,用来补全一行最后可能缺失的单元格
	    private String maxRef = null;
	    
	    private CellDataType nextDataType = CellDataType.SSTINDEX; 
		private final DataFormatter formatter = new DataFormatter(); 
		private short formatIndex; 
		private String formatString; 
		
		//用一个enum表示单元格可能的数据类型
		enum CellDataType{ 
			BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER, DATE, NULL 
		}
		
		private SheetHandler(SharedStringsTable sst) {
			this.sst = sst;
		}
		
		/**
		 * 解析一个element的开始时触发事件
		 */
		public void startElement(String uri, String localName, String name,
				Attributes attributes) throws SAXException {
			
			// c => cell
			if(name.equals("c")) {
				//前一个单元格的位置
				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;
				}
				
			}
			// Clear contents cache
			lastContents = "";
		}
		
		/**
		 * 根据element属性设置数据类型
		 * @param attributes
		 */
		public void setNextDataType(Attributes attributes){ 

			nextDataType = CellDataType.NUMBER; 
			formatIndex = -1; 
			formatString = null; 
			String cellType = attributes.getValue("t"); 
			String cellStyleStr = attributes.getValue("s"); 
			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; 
					//full format is "yyyy-MM-dd hh:mm:ss.SSS";
					formatString = "yyyy-MM-dd";
				} 
				if (formatString == null){ 
					nextDataType = CellDataType.NULL; 
					formatString = BuiltinFormats.getBuiltinFormat(formatIndex); 
				} 
			} 
		}
		
		/**
		 * 解析一个element元素结束时触发事件
		 */
		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 = new XSSFRichTextString(sst.getEntryAt(idx)).toString();
				nextIsString = false;
			}

			// v => contents of a cell
			// Output after we've seen the string contents
			if (name.equals("v")) { 
                String value = this.getDataValue(lastContents.trim(), ""); 
                //补全单元格之间的空单元格
                if(!ref.equals(preRef)){
                	int len = countNullCell(ref, preRef);
                    for(int i=0;i

 

最后输出结果:

书目,作者,主题,语言,分类,阅读计划,
生活在别处,米兰.昆德拉,人生感悟,英语,哲理,3周,
人间词话,王国维,诗词评论,中文,文学,5周,
宽容,房龙,,英语,,,
深入理解计算机系统,,,,计算机,,

你可能感兴趣的:(Java)