通过eventusermodel读取文件
通过eventusermodel读取文件要比使用usermodel复杂得多,但效率也要高不少,因为它要求应用程序一边读取数据,一边处理数据。
eventusermodel实际上模拟了DOM环境下SAX处理XML文档的办法,应用程序首先要注册期望处理的数据,eventusermodel将在遇到匹配的数据结构时回调应用程序注册的方法。使用eventusermodel最大的困难在于你必须熟悉Excel工作簿的内部结构。
在HSSF中,低层次的二进制结构称为记录(Record)。记录有不同的类型,每一种类型由org.apache.poi.hssf.record包中的一个Java类描述。例如,BOFRecord记录表示Workbook或Sheet区域的开始,RowRecord表示有一个行存在并保存其样式信息。
所有具有CellValueRecordInterface接口的记录表示Excel的单元格,包括NumericRecord、LabelSSTRecord和FormulaRecord(还有其他一些,其中部分已被弃置不用,部分用于优化处理,但一般而言,HSSF可以转换它们)。
解析Excel2003 的event user-model必须继承 HSSFListener;
public class UserModelEventListener implements HSSFListener ;
package com.easyway.excel.events;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.CollectionUtils;
import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener;
import org.apache.poi.hssf.eventusermodel.HSSFListener;
import org.apache.poi.hssf.record.BOFRecord;
import org.apache.poi.hssf.record.BlankRecord;
import org.apache.poi.hssf.record.BoolErrRecord;
import org.apache.poi.hssf.record.BoundSheetRecord;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.LabelSSTRecord;
import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.RowRecord;
import org.apache.poi.hssf.record.SSTRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 基于POI HSSF的eventmodel 模型的时间解析方式
* 优点:解析数据相当快。
* 缺点:1.仅仅支持97~2003版本的excel,不支持2007版本的excel。
* 2.只能读Excel中一个Sheet页面。
*
* @Title:
* @Description: 实现TODO
* @Copyright:Copyright (c) 2011
* @Company:易程科技股份有限公司
* @Date:2012-6-14
* @author longgangbai
* @version 1.0
*/
public class UserModelEventListener implements HSSFListener {
private static Logger logger=LoggerFactory.getLogger(UserModelEventListener.class);
private SSTRecord sstrec;
/** Should we output the formula, or the value it has? */
private boolean outputFormulaValues = true;
/** For parsing Formulas */
private SheetRecordCollectingListener workbookBuildingListener;
//当前Sheet的内容
private List<Map<String,Object>> currentSheetDataMap=new ArrayList<Map<String,Object>>();
//列对应的字段
private static String[] trianListheadTitle=new String[]{"trainCode","firstStation","lastStation","startStation","arriveStation","startTime","arriveTime","fistLevelPrice","secondLevelPrice","km","useDate"};
//一行记录
private Map<String,Object> currentSheetRowDataMap=new HashMap<String,Object>();
private int curRowNum=0;
private int ignoreRowNum=1;
private int sheetNo=0;
@Override
public void processRecord(org.apache.poi.hssf.record.Record record) {
switch (record.getSid()) {
case BOFRecord.sid:
BOFRecord bof = (BOFRecord) record;
//顺序进入新的Workbook
if (bof.getType() == bof.TYPE_WORKBOOK) {
logger.debug("开始解析excel 文档.....");
//顺序进入新的Worksheet,因为Event API不会把Excel文件里的所有数据结构都关联起来,
//所以这儿一定要记录现在进入第几个sheet了。
} else if (bof.getType() == bof.TYPE_WORKSHEET) {
//读取新的一个Sheet页
logger.debug("开始解析sheet页面内容...");
System.out.println("sheetNo="+sheetNo);
sheetNo++;
currentSheetDataMap=new ArrayList<Map<String,Object>>();
}
break;
//开始解析Sheet的信息,记录sheet,这儿会把所有的sheet都顺序打印出来,如果有多个sheet的话,可以顺序记入到一个List里
case BoundSheetRecord.sid:
BoundSheetRecord bsr = (BoundSheetRecord) record;
System.out.println("sheetName="+bsr.getSheetname());
logger.debug("New sheet named: " + bsr.getSheetname());
break;
//执行行记录事件
case RowRecord.sid:
RowRecord rowrec = (RowRecord) record;
logger.debug("记录开始, first column at "
+ rowrec.getFirstCol() + " last column at "
+ rowrec.getLastCol());
break;
// SSTRecords store a array of unique strings used in Excel.
case SSTRecord.sid:
sstrec = (SSTRecord) record;
for (int k = 0; k < sstrec.getNumUniqueStrings(); k++) {
logger.debug("String table value " + k + " = "
+ sstrec.getString(k));
}
break;
//发现数字类型的cell,因为数字和日期都是用这个格式,所以下面一定要判断是不是日期格式,另外默认的数字也会被视为日期格式,所以如果是数字的话,一定要明确指定格式!!!!!!!
case NumberRecord.sid:
NumberRecord nr = (NumberRecord) record;
//HSSFDateUtil.isInternalDateFormat(nr.getXFIndex()) 判断是否为时间列
int column=nr.getColumn();
if(column==5||column==6){
addDataAndrChangeRow(nr.getRow(),nr.getColumn(),getTime(nr.getValue()));
}else{
addDataAndrChangeRow(nr.getRow(),nr.getColumn(),(int)nr.getValue());
}
break;
//发现字符串类型,这儿要取字符串的值的话,跟据其index去字符串表里读取
case LabelSSTRecord.sid:
LabelSSTRecord lsr = (LabelSSTRecord)record;
addDataAndrChangeRow(lsr.getRow(),lsr.getColumn(), sstrec.getString(lsr.getSSTIndex()));
logger.debug("文字列:"+sstrec.getString(lsr.getSSTIndex())+", 行:"+lsr.getRow()+", 列:"+lsr.getColumn());
break;
case BoolErrRecord.sid: //解析boolean错误信息
BoolErrRecord ber = (BoolErrRecord)record;
if(ber.isBoolean()){
addDataAndrChangeRow(ber.getRow(),ber.getColumn(), ber.getBooleanValue());
logger.debug("Boolean:"+ber.getBooleanValue()+", 行:"+ber.getRow()+", 列:"+ber.getColumn());
}
if(ber.isError()){
logger.debug("Error:"+ber.getErrorValue()+", 行:"+ber.getRow()+", 列:"+ber.getColumn());
}
break;
//空白记录的信息
case BlankRecord.sid:
BlankRecord br = (BlankRecord)record;
addDataAndrChangeRow(br.getRow(),br.getColumn(), "");
logger.debug("空。 行:"+br.getRow()+", 列:"+br.getColumn());
break;
case FormulaRecord.sid: //数式
FormulaRecord fr = (FormulaRecord)record;
addDataAndrChangeRow(fr.getRow(),fr.getColumn(), fr.getValue());
logger.debug("数字 。 行:"+fr.getRow()+", 列:"+fr.getColumn());
break;
}
}
/**
* HH:MM格式时间的数字转换方法</li>
* @param sNum
* @return
*/
private static String getTime(double daynum)
{
double totalSeconds=daynum*86400.0D;
//总的分钟数
int seconds =(int)totalSeconds/60;
//实际小时数
int hours =seconds/60;
int minutes = seconds-hours*60;
//剩余的实际分钟数
StringBuffer sb=new StringBuffer();
if(String.valueOf(hours).length()==1){
sb.append("0"+hours);
}else{
sb.append(hours);
}
sb.append(":");
if(String.valueOf(minutes).length()==1){
sb.append("0"+minutes);
}else{
sb.append(minutes);
}
return sb.toString();
}
/**
* 添加数据记录并检查是否换行
* @param row 实际当前行号
* @param col 实际记录当前列
* @param value 当前cell的值
*/
public void addDataAndrChangeRow(int row,int col,Object value){
//当前行如果大于实际行表示改行忽略,不记录
if(curRowNum!=row){
if(CollectionUtils.isEmpty(currentSheetDataMap)){
currentSheetDataMap=new ArrayList<Map<String,Object>>();
}
currentSheetDataMap.add(currentSheetRowDataMap);
logger.debug("行号:"+curRowNum +" 行内容:"+currentSheetRowDataMap.toString());
logger.debug("\n");
currentSheetRowDataMap=new HashMap<String,Object>();
currentSheetRowDataMap.put(trianListheadTitle[col], value);
logger.debug(row+":"+col+" "+value+"\r");
curRowNum=row;
}else{
currentSheetRowDataMap.put(trianListheadTitle[col], value);
logger.debug(row+":"+col+" "+value+"\r");
}
}
public List<Map<String, Object>> getCurrentSheetDataMap() {
return currentSheetDataMap;
}
public void setCurrentSheetDataMap(List<Map<String, Object>> currentSheetDataMap) {
this.currentSheetDataMap = currentSheetDataMap;
}
public Map<String, Object> getCurrentSheetRowDataMap() {
return currentSheetRowDataMap;
}
public void setCurrentSheetRowDataMap(Map<String, Object> currentSheetRowDataMap) {
this.currentSheetRowDataMap = currentSheetRowDataMap;
}
public int getCurRowNum() {
return curRowNum;
}
public void setCurRowNum(int curRowNum) {
this.curRowNum = curRowNum;
}
public int getIgnoreRowNum() {
return ignoreRowNum;
}
public void setIgnoreRowNum(int ignoreRowNum) {
this.ignoreRowNum = ignoreRowNum;
}
}
测试代码如下:
package com.easyway.excel.events;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
import org.apache.poi.hssf.eventusermodel.HSSFRequest;
import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener;
import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
public class UserModelEventMain {
/**
* 解析Excel2003的事件解析格式
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
UserModelEventListener xlsEventListener=new UserModelEventListener();
MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(
xlsEventListener);
FormatTrackingHSSFListener formatListener = new FormatTrackingHSSFListener(listener);
//创建一个excel输入流
FileInputStream fin = new FileInputStream("C:\\station2station.xls");
//创建一个 org.apache.poi.poifs.filesystem.Filesystem
POIFSFileSystem poifs = new POIFSFileSystem(fin);
//将excel 2003格式POI文档输入流
InputStream din = poifs.createDocumentInputStream("Workbook");
//这儿为所有类型的Record都注册了监听器,如果需求明确的话,可以用addListener方法,并指定所需的Record类型
HSSFRequest req = new HSSFRequest();
//添加监听记录的事件
req.addListenerForAllRecords(xlsEventListener);
boolean outputFormulaValues=true;
if (outputFormulaValues) {
req.addListenerForAllRecords(formatListener);
} else {
SheetRecordCollectingListener workbookBuildingListener = new SheetRecordCollectingListener(
formatListener);
req.addListenerForAllRecords(workbookBuildingListener);
}
//创建时间工厂
HSSFEventFactory factory = new HSSFEventFactory();
//处理基于时间文档流
factory.processEvents(req, din);
//关闭文件流
fin.close();
//关闭基于POI文档流
din.close();
List<Map<String, Object>> sheetDataList=xlsEventListener.getCurrentSheetDataMap();
System.out.println("Excel Sheet记录数"+sheetDataList.size()+" 内容"+sheetDataList.toString());
}
}
测试excel文件如下: