秒懂POI解析excel,SAXParser解析大xlsx,XSSFReader处理包括被忽略的空单元格处理
2018-04-01 18:58
1616 查看
poi常用解析excel文件
excel分97-03格式xls和07格式xlsx,官网对于这两种的说明POI-HSSF and POI-XSSF
最新的是SXSSF
【原理】
poi先将excel转为xml,而后是使用SAXParser解析器,解析xml文件得到excel的数据
xml解析一般是先转dom树,然后操作,【方便随意遍历】,但是这需要将全部xml加载处理,适合小的xml,或者配置类xml
xml文件到数百M或上G的量,全部加载效率低,无法生成完整dom树操作,所以SAX解析器是循环读取一定长度处理,读到一个标签就会回调一个用户方法处理,这样减小内存。【适合大量数据导入,不能回头遍历以前的xml,需要自己实现处理xml内读取的数据关系】
excel转换后的完整xml例子,test.xml
【XSSFReader空单元格,空行问题,从上面的xml可以看出】poi转换excel为xml会忽略空单元格(不是单元格内容为空格,是单元格没有内容)和空行,导致转换后的【数据错位问题】,需要自己实现判断空单元格和空行处理(根据excel的行列号,比如B1, D1则表明C1是空单元格,行row的行列号由L3(L是列号,3是行号),到AB7,表明4,5,6是空行)
【SAXParser解析器DefaultHandler】
从上面的介绍可以大致了解poi处理excel的过程,我们要做的就是覆盖实现解析的方法,来达到自己的需求
自己的Handler继承DefaultHandler,覆盖一些方法
xml标签的成对的,有开始,有结束
startDocument是?xml标签的回调处理方法
startElement方法是读到一个xml开始标签时的回调处理方法
endElement是标签结束的回调处理方法
characters方法是处理xml中的v标签中间的内容的回调处理方法
【注意xml中的c与v标签】
c就是cell单元格,c的属性r是行列号,t是类型,当t是s,表示是SST(SharedStringsTable) 的索引,其他类型很多,不一一列举,打开调试看看完整xml内容,注意在自己的Handler中处理,比如单元格是日期格式等等
v是单元内容【或SST索引】,注意SST索引的取值方式
以下是一个基本的处理类,可以很好理解poi解析excel,可以根据需要完善一下,【包含空单元格处理,没有空行处理】
需要用到的类,pom.xml
excel分97-03格式xls和07格式xlsx,官网对于这两种的说明POI-HSSF and POI-XSSF
最新的是SXSSF
【原理】
poi先将excel转为xml,而后是使用SAXParser解析器,解析xml文件得到excel的数据
xml解析一般是先转dom树,然后操作,【方便随意遍历】,但是这需要将全部xml加载处理,适合小的xml,或者配置类xml
xml文件到数百M或上G的量,全部加载效率低,无法生成完整dom树操作,所以SAX解析器是循环读取一定长度处理,读到一个标签就会回调一个用户方法处理,这样减小内存。【适合大量数据导入,不能回头遍历以前的xml,需要自己实现处理xml内读取的数据关系】
excel转换后的完整xml例子,test.xml
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"> <dimension ref="A1:AB7"/> <sheetViews> <sheetView tabSelected="1" workbookViewId="0"> <selection activeCell="L3" sqref="L3"/> </sheetView> </sheetViews> <sheetFormatPr defaultRowHeight="13.8" x14ac:dyDescent="0.25"/> <cols> <col min="12" max="12" width="9.109375" bestFit="1" customWidth="1"/> </cols> <sheetData> <row r="1" spans="1:28" x14ac:dyDescent="0.25"> <c r="A1"> <v>1</v> </c> <c r="B1"> <v>2</v> </c> <c r="D1"> <v>4</v> </c> <c r="G1"> <v>7</v> </c> <c r="H1" t="s"> <v>0</v> </c> <c r="I1" t="s"> <v>4</v> </c> <c r="K1"> <v>32423</v> </c> <c r="U1"> <v>78979</v> </c> <c r="Y1" t="s"> <v>3</v> </c> </row> <row r="2" spans="1:28" x14ac:dyDescent="0.25"> <c r="B2"> <v>22</v> </c> <c r="C2"> <v>33</v> </c> <c r="E2"> <v>55</v> </c> <c r="F2" t="s"> <v>1</v> </c> <c r="Q2" t="s"> <v>2</v> </c> </row> <row r="3" spans="1:28" x14ac:dyDescent="0.25"> <c r="L3" s="1"> <v>201287</v> </c> </row> <row r="7" spans="1:28" x14ac:dyDescent="0.25"> <c r="AB7"> <v>123131</v> </c> </row> </sheetData> <phoneticPr fontId="1" type="noConversion"/> <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/> <pageSetup paperSize="9" orientation="portrait" horizontalDpi="1200" verticalDpi="1200" r:id="rId1"/> </worksheet>
【XSSFReader空单元格,空行问题,从上面的xml可以看出】poi转换excel为xml会忽略空单元格(不是单元格内容为空格,是单元格没有内容)和空行,导致转换后的【数据错位问题】,需要自己实现判断空单元格和空行处理(根据excel的行列号,比如B1, D1则表明C1是空单元格,行row的行列号由L3(L是列号,3是行号),到AB7,表明4,5,6是空行)
【SAXParser解析器DefaultHandler】
从上面的介绍可以大致了解poi处理excel的过程,我们要做的就是覆盖实现解析的方法,来达到自己的需求
自己的Handler继承DefaultHandler,覆盖一些方法
xml标签的成对的,有开始,有结束
startDocument是?xml标签的回调处理方法
startElement方法是读到一个xml开始标签时的回调处理方法
endElement是标签结束的回调处理方法
characters方法是处理xml中的v标签中间的内容的回调处理方法
【注意xml中的c与v标签】
c就是cell单元格,c的属性r是行列号,t是类型,当t是s,表示是SST(SharedStringsTable) 的索引,其他类型很多,不一一列举,打开调试看看完整xml内容,注意在自己的Handler中处理,比如单元格是日期格式等等
v是单元内容【或SST索引】,注意SST索引的取值方式
以下是一个基本的处理类,可以很好理解poi解析excel,可以根据需要完善一下,【包含空单元格处理,没有空行处理】
//单文件示例代码,转换结果在List<List<String>> container import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.xssf.eventusermodel.XSSFReader; import org.apache.poi.xssf.model.SharedStringsTable; 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 java.io.File; import java.io.InputStream; import java.util.ArrayList; import java.util.List; public class Test2 { public static void main(String[] args) throws Exception{ File test = new File("."); String file = test.getAbsolutePath()+"/src/main/resources/empty_cell 中文名.xlsx"; OPCPackage pkg = OPCPackage.open(file); XSSFReader r = new XSSFReader( pkg ); InputStream in = r.getSheet("rId1"); //查看转换的xml原始文件,方便理解后面解析时的处理, // 注意:如果打开注释,下面parse()就读不到流的内容了 Test2.streamOut(in); //下面是SST 的索引会用到的 SharedStringsTable sst = r.getSharedStringsTable(); //sst.writeTo(System.out); XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser"); List<List<String>> container = new ArrayList<>(); parser.setContentHandler(new Myhandler(sst,container)); InputSource inputSource = new InputSource(in); parser.parse(inputSource); in.close(); Test2.printContainer(container); } public static void printContainer(List<List<String>> container) { for(List<String> stringList:container) { for(String str:stringList) { System.out.printf("%15s",str+" | "); } System.out.println(""); } } //读取流,查看文件内容 public static void streamOut(InputStream in) throws Exception{ byte[] buf = new byte[1024]; int len; while ((len=in.read(buf))!=-1){ System.out.write(buf,0,len); } } } class Myhandler extends DefaultHandler{ //取SST 的索引对应的值 private SharedStringsTable sst; public void setSst(SharedStringsTable sst) { this.sst = sst; } //解析结果保存 private List<List<String>> container; public Myhandler(SharedStringsTable sst, List<List<String>> container) { this.sst = sst; this.container = container; } private String lastContents; //有效数据矩形区域,A1:Y2 private String dimension; //根据dimension得出每行的数据长度 private int longest; //上个有内容的单元格id,判断空单元格 private String lastRowid; //行数据保存 private List<String> currentRow; //单元格内容是SST 的索引 private boolean isSSTIndex=false; @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { // System.out.println("startElement:"+qName); if (qName.equals("dimension")){ dimension = attributes.getValue("ref"); longest = covertRowIdtoInt(dimension.substring(dimension.indexOf(":")+1) ); } //行开始 if (qName.equals("row")) { currentRow = new ArrayList<>(); } if (qName.equals("c")) { String rowId = attributes.getValue("r"); //空单元判断,添加空字符到list if (lastRowid!=null) { int gap = covertRowIdtoInt(rowId)-covertRowIdtoInt(lastRowid); for(int i=0;i<gap-1;i++) { currentRow.add(""); } }else{ //第一个单元格可能不是在第一列 if (!"A1".equals(rowId)) { for(int i=0;i<covertRowIdtoInt(rowId)-1;i++) { currentRow.add(""); } } } lastRowid = rowId; //判断单元格的值是SST 的索引,不能直接characters方法取值 if (attributes.getValue("t")!=null && attributes.getValue("t").equals("s")) { isSSTIndex = true; }else{ isSSTIndex = false; } } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { // System.out.println("endElement:"+qName); //行结束,存储一行数据 if (qName.equals("row")) { //判断最后一个单元格是否在最后,补齐列数 if(covertRowIdtoInt(lastRowid)<longest){ for(int i=0;i<longest- covertRowIdtoInt(lastRowid);i++) { currentRow.add(""); } } container.add(currentRow); lastRowid=null; } //单元格内容标签结束,characters方法会被调用处理内容 if (qName.equals("v")) { //单元格的值是SST 的索引 if (isSSTIndex){ String sstIndex = lastContents.toString(); try { int idx = Integer.parseInt(sstIndex); XSSFRichTextString rtss = new XSSFRichTextString( sst.getEntryAt(idx)); lastContents = rtss.toString(); currentRow.add(lastContents); } catch (NumberFormatException ex) { System.out.println(lastContents); } }else { currentRow.add(lastContents); } } } /** * 获取element的文本数据 */ public void characters(char[] ch, int start, int length) throws SAXException { lastContents = new String(ch, start, length); } /** * 列号转数字 AB7-->28 第28列 * @param rowId * @return */ public static int covertRowIdtoInt(String rowId){ int firstDigit = -1; for (int c = 0; c < rowId.length(); ++c) { if (Character.isDigit(rowId.charAt(c))) { firstDigit = c; break; } } //AB7-->AB //AB是列号, 7是行号 String newRowId = rowId.substring(0,firstDigit); int num = 0; int result = 0; int length = newRowId.length(); for(int i = 0; i < length; i++) { //先取最低位,B char ch = newRowId.charAt(length - i - 1); //B表示的十进制2,ascii码相减,以A的ascii码为基准,A表示1,B表示2 num = (int)(ch - 'A' + 1) ; //列号转换相当于26进制数转10进制 num *= Math.pow(26, i); result += num; } return result; } public static void main(String[] args) { System.out.println(Myhandler.covertRowIdtoInt("AB7")); } }
需要用到的类,pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.dddd</groupId> <artifactId>poisaxxls</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.17</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.17</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>3.17</version> </dependency> <dependency> <groupId>xerces</groupId> <artifactId>xercesImpl</artifactId> <version>2.11.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
相关文章推荐
- POI以SAX方式解析Excel2007大文件(包含空单元格的处理)
- POI以SAX方式解析Excel2007大文件(包含空单元格的处理) Java生成CSV文件实例详解
- POI使用SAX处理大量数据的xlsx格式的Excel文件
- 利用POI框架的SAX方式处理大数据2007版Excel(xlsx)
- 利用POI框架的SAX方式处理大数据2007版Excel(xlsx)
- java 基于poi导入excel(可解析xlsx)
- Java 开发之:POI XLS和XLSX兼容解析及单元格数据转换工具代码
- 通过poi实现解析并读取excel文件(包含xls、xlsx后缀)
- POI使用:用poi接口不区分xls/xlsx格式解析Excel文档(41种日期格式解析方法,5种公式结果类型解析方法,3种常用数值类型精度控制办法)
- 使用JAXP进行SAX解析(XMLReaderFactory、XMLReader 、SAXParserFactory与SAXParser)
- poi创建、解析Excel(还有高版本的xlsx格式的Excel创建)
- 使用JAXP进行SAX解析(XMLReaderFactory、XMLReader 、SAXParserFactory与SAXParser)
- ExcelParser ,Excel解析的工具类(正对解析xlsx)
- POI解析Excel(.xls)和Excel2007(.xlsx)
- POI 操作EXCEL(一)解析EXCEL 自动识别xls和xlsx
- java poi解析excel报错处理
- 使用poi来解析Excel的xls和xlsx。
- java使用poi解析或处理excel的时候,如何防止数字变成科学计数法的形式
- java使用poi解析或处理excel的时候,如何防止数字变成科学计数法的形式
- 使用JAXP进行SAX解析(XMLReaderFactory、XMLReader 、SAXParserFactory与SAXParser)