POI通过事件解析Excel,空单元格问题

J2EE 码拜 9年前 (2015-07-18) 2770次浏览

我今天发现代码里面有个这样的问题,就是POI事件解析Excel的时候,空单元格会被忽略,请问下知道的筒子,这种情况怎么处理,注意是事件读取,附代码

package com.its.fedex.msg.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
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.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

import com.its.fedex.msg.exception.ExcelParseException;
import com.its.fedex.msg.service.IRowHandler;

public class Excel2007Reader extends DefaultHandler
{
	/**
	 * 共享字符串表
	 */
	private SharedStringsTable sst;

	/**
	 * 上一次的内容
	 */
	private String lastContents;

	/**
	 * 字符串标识
	 */
	private boolean nextIsString;

	/**
	 * 工作表索引
	 */
	private int sheetIndex = -1;

	/**
	 * 行集合
	 */
	private List<String> rowlist = new ArrayList<String>();

	/**
	 * 当前行
	 */
	private int curRow = 0;

	/**
	 * 当前列
	 */
	private int curCol = 0;

	/**
	 * T元素标识
	 */
	private boolean isTElement;

	/**
	 * Excel数据逻辑处理
	 */
	private IRowHandler rowHandler;

	/**
	 * 异常信息,如果为空则表示没有异常
	 */
	private String exceptionMessage;

	/**
	 * 单元格数据类型,默认为字符串类型
	 */
	private CellDataType nextDataType = CellDataType.SSTINDEX;

	private final DataFormatter formatter = new DataFormatter();

	private short formatIndex;

	private String formatString;

	/**
	 * 单元格
	 */
	private StylesTable stylesTable;

	public void setRowReader(IRowHandler rowHandler)
	{
		this.rowHandler = rowHandler;
	}

	/**
	 * 遍历工作簿中所有的电子表格
	 * 
	 * @param filename
	 * @throws IOException
	 * @throws OpenXML4JException
	 * @throws SAXException
	 * @throws Exception
	 */
	public void process(String filename) throws IOException, OpenXML4JException, SAXException
	{
		OPCPackage pkg = OPCPackage.open(filename);
		XSSFReader xssfReader = new XSSFReader(pkg);
		stylesTable = xssfReader.getStylesTable();
		SharedStringsTable sst = xssfReader.getSharedStringsTable();
		XMLReader parser = this.fetchSheetParser(sst);
		Iterator<InputStream> sheets = xssfReader.getSheetsData();
		while (sheets.hasNext())
		{
			curRow = 0;
			sheetIndex++;
			InputStream sheet = sheets.next();
			InputSource sheetSource = new InputSource(sheet);
			parser.parse(sheetSource);
			sheet.close();
		}
	}

	public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException
	{
		XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
		this.sst = sst;
		parser.setContentHandler(this);
		return parser;
	}

	@Override
	public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException
	{
		// c => 单元格
		if ("c".equals(name))
		{
			// 设定单元格类型
			this.setNextDataType(attributes);
		}

		// 当元素为t时
		if ("t".equals(name))
		{
			isTElement = true;
		}
		else
		{
			isTElement = false;
		}

		// 置空
		lastContents = "";
	}

	/**
	 * 单元格中的数据可能的数据类型
	 */
	enum CellDataType
	{
		BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER, DATE, NULL
	}

	/**
	 * 处理数据类型
	 * 
	 * @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;
				formatString = "yyyy-MM-dd hh:mm:ss.SSS";
			}

			if (formatString == null)
			{
				nextDataType = CellDataType.NULL;
				formatString = BuiltinFormats.getBuiltinFormat(formatIndex);
			}
		}
	}

	/**
	 * 对解析出来的数据进行类型处理
	 * 
	 * @param value
	 *            单元格的值(这时候是一串数字)
	 * @param thisStr
	 *            一个空字符串
	 * @return
	 */
	@SuppressWarnings("deprecation")
	public String getDataValue(String value, String thisStr)
	{
		switch (nextDataType)
		{
			// 这几个的顺序不能随便交换,交换了很可能会导致数据错误
			case BOOL:
				char first = value.charAt(0);
				thisStr = first == ""0"" ? "FALSE" : "TRUE";
				break;
			case ERROR:
				thisStr = ""ERROR:" + value.toString() + """"";
				break;
			case FORMULA:
				thisStr = """"" + value.toString() + """"";
				break;
			case INLINESTR:
				XSSFRichTextString rtsi = new XSSFRichTextString(value.toString());

				thisStr = rtsi.toString();
				rtsi = null;
				break;
			case SSTINDEX:
				String sstIndex = value.toString();
				try
				{
					int idx = Integer.parseInt(sstIndex);
					XSSFRichTextString rtss = new XSSFRichTextString(sst.getEntryAt(idx));
					thisStr = rtss.toString();
					rtss = null;
				}
				catch (NumberFormatException ex)
				{
					thisStr = value.toString();
				}
				break;
			case NUMBER:
				if (formatString != null)
				{
					thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString)
							.trim();
				}
				else
				{
					thisStr = value;
				}

				thisStr = thisStr.replace("_", "").trim();
				break;
			case DATE:
				thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString);

				// 对日期字符串作特殊处理
				thisStr = thisStr.replace(" ", "T");
				break;
			default:
				thisStr = " ";

				break;
		}

		return thisStr;
	}

	@Override
	public void endElement(String uri, String localName, String name) throws SAXException
	{
		// 根据SST的索引值的到单元格的真正要存储的字符串
		// 这时characters()方法可能会被调用多次
		if (nextIsString)
		{
			int idx = Integer.parseInt(lastContents);
			lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString();
		}

		// t元素也包含字符串
		if (isTElement)
		{
			// 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符
			String value = lastContents.trim();
			rowlist.add(curCol, value);
			curCol++;
			isTElement = false;
		}
		else if ("v".equals(name))
		{
			// v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引
			String value = this.getDataValue(lastContents.trim(), "");

			rowlist.add(curCol, value);
			curCol++;
		}
		else
		{
			// 如果标签名称为 row ,这说明已到行尾,调用 optRows() 方法
			if (name.equals("row"))
			{
				try
				{
					rowHandler.getRows(curRow, rowlist);
				}
				catch (ExcelParseException e)
				{
					exceptionMessage = e.getMessage();
				}
				rowlist.clear();
				curRow++;
				curCol = 0;
			}
		}
	}

	@Override
	public void characters(char[] ch, int start, int length) throws SAXException
	{
		// 得到单元格内容的值
		lastContents += new String(ch, start, length);
	}

	/**
	 * @return the exceptionMessage
	 */
	public String getExceptionMessage()
	{
		return exceptionMessage;
	}
}

#1

没人知道啊,擦

#2

100分给我 我告诉你
20分

#3

private List<Map> parseworkbookVoucher(HSSFWorkbook workbook){
		//存放凭证主表
		List<Map> VoucherList=new ArrayList<Map>();
	    int columnNum = 0;  
	    Sheet sheet = workbook.getSheetAt(0);  
	    if(sheet.getRow(0)!=null){  
	        columnNum = sheet.getRow(0).getLastCellNum()-sheet.getRow(0).getFirstCellNum();  
	    }  
	    Row rowkey=sheet.getRow(0);
	    if(columnNum>1){  
	      for (int j = 2; j <= sheet.getLastRowNum(); j++) {
	    	  Row row=sheet.getRow(j);
	    	  Map<String, Object>  Vouchermap =new HashMap<String, Object>();
	          for(int i=0;i<columnNum;i++){  
	             Cell cell = row.getCell(i, Row.CREATE_NULL_AS_BLANK);  
	             Cell cellkey = rowkey.getCell(i);  
	             switch(cell.getCellType()){  
	               case Cell.CELL_TYPE_BLANK:  
	            	   Vouchermap.put(cellkey.getStringCellValue().trim(), "");
	                   break;  
	               case Cell.CELL_TYPE_BOOLEAN:  
	            	   Vouchermap.put(cellkey.getStringCellValue().trim(), "");
	                   break;  
	               case Cell.CELL_TYPE_NUMERIC: 
	            	   if(DateUtil.isCellDateFormatted(cell)){  
	            		   //System.out.println("日期:"+cell.getDateCellValue());
	            		   Vouchermap.put(cellkey.getStringCellValue().trim(), dateformate(cell.getDateCellValue()));  
	            	   }else{
	            		   cell.setCellType(Cell.CELL_TYPE_STRING);  
	            		   //System.out.println("*********"+cellkey.getStringCellValue());
	            		   //System.out.println("*********"+cell.getStringCellValue());
	            		   //DecimalFormat df = new DecimalFormat("###0.00");
	            		   Vouchermap.put(cellkey.getStringCellValue().trim(), cell.getStringCellValue());
	            	   }
	            	   break;  
	               case Cell.CELL_TYPE_STRING:  
	            	   Vouchermap.put(cellkey.getStringCellValue().trim(), cell.getStringCellValue());
	                   break;  
	               case Cell.CELL_TYPE_ERROR:  
	            	   Vouchermap.put(cellkey.getStringCellValue().trim(), "");
	                   break;    
	             }  
	          }   
	          //Vouchermap.put("Table", "Co_Relationdetails1");
	          if(null!=Vouchermap.get("cOrgnID")&&!Vouchermap.get("cOrgnID").equals("")){
	        	  VoucherList.add(Vouchermap);
	          }
	         
	      }  
	    }  
	    for (int m = 0; m < VoucherList.size(); m++) {
			System.out.println(VoucherList.size());
			Map map=VoucherList.get(m);
			System.out.println("模板数据:"+map);
		}
	    return VoucherList; 
	}

换一个遍历方式,楼主的遍历可能涉及到一些迭代器,其中可能进行了一些处理,具体细节不太了解

#4

回复楼:

我也很想100分都给你,可是你的做法是基于UserModel解析的,我用的是事件解析,因为数据量比较大,没办法

#5

楼主能否说下事件解析Excel是什么意思?
基于事件?what?

#6

回复楼:

Event,就是一个先把Excel弄成DOM树的结构,然后用SAX解析,你那种UserModle的解析方式是另外一种常用的方式,但是碰上数据量比较大的情况,就会内存溢出,参看这个博客http://blog.csdn.net/goodkuang2012/article/details/7350985

10分

#7

单元格是否为空,可以根据

cell.getCellType() == HSSFCell.CELL_TYPE_BLANK

来判断

#8

回复楼:

没有Cell对象

70分

#9

poi好久没用了都忘了,应该是有Cell对象的,poi操作的对象不就是sheet  row cell 这样的

#10

回复楼:

那是前面说的UserModel的解析方式,那种方式的确有,操作简便,就是很容易内存溢出

#11

昨天自己解决了,结贴
attributes.getValue(“r”);

#12

attributes.getValue(“r”);  在什么地方判断啊? 我也遇到空格的问题了

#13

请问,这个问题怎么解决啊

#14

这个能读excel中的“真空”值吗?

#15

回复11楼:

怎么解决的?

#16

回复15楼:

我已解决,有问题问我

#17

DefaultHandle接口里面仔细找下,有一个方法就可以满足你。加油,另外03頒的也支持时间驱动。

#18

回复15楼:

请问这个空单元格问题是怎么解决的啊

#19

attributes.getValue(“r”);
nmb  装
解决就说
孩子啊哪里装nmb
楼主wcnm

#20

刚才我调试了一下,attributes.getValue(“r”);是获取单元格的坐标例如A1,B1,D1,把这些跟读取出来的数值绑定一下,哪个没绑定就给他设为一个控制就行了,哈哈

#21

@wujiangqing123
具体如何处理,是否可以的供一下代吗

CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明POI通过事件解析Excel,空单元格问题
喜欢 (0)
[1034331897@qq.com]
分享 (0)