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

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

if(name.equals("c")) { 
//前一个单元格的位置 
if(preRef == null){ 
preRef = attributes.getValue("r"); 
}else{ 
preRef = ref; 

//当前单元格的位置 
ref = attributes.getValue("r"); 


从c中是判断不出来是否有空格的,所以ref 和 preRef直接是始终判断不出来的,我不知道你是怎么判断的。


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

   

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

   程序源码:

  

[java]  view plain  copy
  1. package test;  
  2.   
  3. import java.io.BufferedWriter;  
  4. import java.io.FileOutputStream;  
  5. import java.io.IOException;  
  6. import java.io.InputStream;  
  7. import java.io.OutputStreamWriter;  
  8. import java.sql.SQLException;  
  9. import java.util.ArrayList;  
  10. import java.util.Iterator;  
  11. import java.util.List;  
  12.   
  13. import org.apache.poi.openxml4j.opc.OPCPackage;  
  14. import org.apache.poi.ss.usermodel.BuiltinFormats;  
  15. import org.apache.poi.ss.usermodel.DataFormatter;  
  16. import org.apache.poi.xssf.eventusermodel.XSSFReader;  
  17. import org.apache.poi.xssf.model.SharedStringsTable;  
  18. import org.apache.poi.xssf.model.StylesTable;  
  19. import org.apache.poi.xssf.usermodel.XSSFCellStyle;  
  20. import org.apache.poi.xssf.usermodel.XSSFRichTextString;  
  21. import org.xml.sax.Attributes;  
  22. import org.xml.sax.ContentHandler;  
  23. import org.xml.sax.InputSource;  
  24. import org.xml.sax.SAXException;  
  25. import org.xml.sax.XMLReader;  
  26. import org.xml.sax.helpers.DefaultHandler;  
  27. import org.xml.sax.helpers.XMLReaderFactory;  
  28.   
  29. public class ExampleEventUserModel {  
  30.       
  31.     private static StylesTable stylesTable;  
  32.       
  33.     /** 
  34.      * 处理一个sheet 
  35.      * @param filename 
  36.      * @throws Exception 
  37.      */  
  38.     public void processOneSheet(String filename) throws Exception {  
  39.           
  40.         OPCPackage pkg = OPCPackage.open(filename);  
  41.         XSSFReader r = new XSSFReader( pkg );  
  42.         stylesTable = r.getStylesTable();   
  43.         SharedStringsTable sst = r.getSharedStringsTable();  
  44.   
  45.         XMLReader parser = fetchSheetParser(sst);  
  46.   
  47.         // Seems to either be rId# or rSheet#  
  48.         InputStream sheet2 = r.getSheet("rId1");  
  49.         InputSource sheetSource = new InputSource(sheet2);  
  50.         parser.parse(sheetSource);  
  51.         sheet2.close();  
  52.     }  
  53.   
  54.     /** 
  55.      * 处理所有sheet 
  56.      * @param filename 
  57.      * @throws Exception 
  58.      */  
  59.     public void processAllSheets(String filename) throws Exception {  
  60.           
  61.         OPCPackage pkg = OPCPackage.open(filename);  
  62.         XSSFReader r = new XSSFReader( pkg );  
  63.         SharedStringsTable sst = r.getSharedStringsTable();  
  64.           
  65.         XMLReader parser = fetchSheetParser(sst);  
  66.   
  67.         Iterator<InputStream> sheets = r.getSheetsData();  
  68.         while(sheets.hasNext()) {  
  69.             System.out.println("Processing new sheet:\n");  
  70.             InputStream sheet = sheets.next();  
  71.             InputSource sheetSource = new InputSource(sheet);  
  72.             parser.parse(sheetSource);  
  73.             sheet.close();  
  74.             System.out.println("");  
  75.         }  
  76.     }  
  77.   
  78.     /** 
  79.      * 获取解析器 
  80.      * @param sst 
  81.      * @return 
  82.      * @throws SAXException 
  83.      */  
  84.     public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {  
  85.         XMLReader parser =  
  86.             XMLReaderFactory.createXMLReader(  
  87.                     "org.apache.xerces.parsers.SAXParser"  
  88.             );  
  89.         ContentHandler handler = new SheetHandler(sst);  
  90.         parser.setContentHandler(handler);  
  91.         return parser;  
  92.     }  
  93.   
  94.     /**  
  95.      * 自定义解析处理器 
  96.      * See org.xml.sax.helpers.DefaultHandler javadocs  
  97.      */  
  98.     private static class SheetHandler extends DefaultHandler {  
  99.           
  100.         private SharedStringsTable sst;  
  101.         private String lastContents;  
  102.         private boolean nextIsString;  
  103.           
  104.         private List<String> rowlist = new ArrayList<String>();   
  105.         private int curRow = 0;   
  106.         private int curCol = 0;  
  107.           
  108.         //定义前一个元素和当前元素的位置,用来计算其中空的单元格数量,如A6和A8等  
  109.         private String preRef = null, ref = null;  
  110.         //定义该文档一行最大的单元格数,用来补全一行最后可能缺失的单元格  
  111.         private String maxRef = null;  
  112.           
  113.         private CellDataType nextDataType = CellDataType.SSTINDEX;   
  114.         private final DataFormatter formatter = new DataFormatter();   
  115.         private short formatIndex;   
  116.         private String formatString;   
  117.           
  118.         //用一个enum表示单元格可能的数据类型  
  119.         enum CellDataType{   
  120.             BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER, DATE, NULL   
  121.         }  
  122.           
  123.         private SheetHandler(SharedStringsTable sst) {  
  124.             this.sst = sst;  
  125.         }  
  126.           
  127.         /** 
  128.          * 解析一个element的开始时触发事件 
  129.          */  
  130.         public void startElement(String uri, String localName, String name,  
  131.                 Attributes attributes) throws SAXException {  
  132.               
  133.             // c => cell  
  134.             if(name.equals("c")) {  
  135.                 //前一个单元格的位置  
  136.                 if(preRef == null){  
  137.                     preRef = attributes.getValue("r");  
  138.                 }else{  
  139.                     preRef = ref;  
  140.                 }  
  141.                 //当前单元格的位置  
  142.                 ref = attributes.getValue("r");  
  143.                   
  144.                 this.setNextDataType(attributes);   
  145.                   
  146.                 // Figure out if the value is an index in the SST  
  147.                 String cellType = attributes.getValue("t");  
  148.                 if(cellType != null && cellType.equals("s")) {  
  149.                     nextIsString = true;  
  150.                 } else {  
  151.                     nextIsString = false;  
  152.                 }  
  153.                   
  154.             }  
  155.             // Clear contents cache  
  156.             lastContents = "";  
  157.         }  
  158.           
  159.         /** 
  160.          * 根据element属性设置数据类型 
  161.          * @param attributes 
  162.          */  
  163.         public void setNextDataType(Attributes attributes){   
  164.   
  165.             nextDataType = CellDataType.NUMBER;   
  166.             formatIndex = -1;   
  167.             formatString = null;   
  168.             String cellType = attributes.getValue("t");   
  169.             String cellStyleStr = attributes.getValue("s");   
  170.             if ("b".equals(cellType)){   
  171.                 nextDataType = CellDataType.BOOL;  
  172.             }else if ("e".equals(cellType)){   
  173.                 nextDataType = CellDataType.ERROR;   
  174.             }else if ("inlineStr".equals(cellType)){   
  175.                 nextDataType = CellDataType.INLINESTR;   
  176.             }else if ("s".equals(cellType)){   
  177.                 nextDataType = CellDataType.SSTINDEX;   
  178.             }else if ("str".equals(cellType)){   
  179.                 nextDataType = CellDataType.FORMULA;   
  180.             }  
  181.             if (cellStyleStr != null){   
  182.                 int styleIndex = Integer.parseInt(cellStyleStr);   
  183.                 XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);   
  184.                 formatIndex = style.getDataFormat();   
  185.                 formatString = style.getDataFormatString();   
  186.                 if ("m/d/yy" == formatString){   
  187.                     nextDataType = CellDataType.DATE;   
  188.                     //full format is "yyyy-MM-dd hh:mm:ss.SSS";  
  189.                     formatString = "yyyy-MM-dd";  
  190.                 }   
  191.                 if (formatString == null){   
  192.                     nextDataType = CellDataType.NULL;   
  193.                     formatString = BuiltinFormats.getBuiltinFormat(formatIndex);   
  194.                 }   
  195.             }   
  196.         }  
  197.           
  198.         /** 
  199.          * 解析一个element元素结束时触发事件 
  200.          */  
  201.         public void endElement(String uri, String localName, String name)  
  202.                 throws SAXException {  
  203.             // Process the last contents as required.  
  204.             // Do now, as characters() may be called more than once  
  205.             if(nextIsString) {  
  206.                 int idx = Integer.parseInt(lastContents);  
  207.                 lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString();  
  208.                 nextIsString = false;  
  209.             }  
  210.   
  211.             // v => contents of a cell  
  212.             // Output after we've seen the string contents  
  213.             if (name.equals("v")) {   
  214.                 String value = this.getDataValue(lastContents.trim(), "");   
  215.                 //补全单元格之间的空单元格  
  216.                 if(!ref.equals(preRef)){  
  217.                     int len = countNullCell(ref, preRef);  
  218.                     for(int i=0;i<len;i++){  
  219.                         rowlist.add(curCol, "");  
  220.                         curCol++;  
  221.                     }  
  222.                 }  
  223.                 rowlist.add(curCol, value);  
  224.                 curCol++;   
  225.             }else {   
  226.                 //如果标签名称为 row,这说明已到行尾,调用 optRows() 方法   
  227.                 if (name.equals("row")) {  
  228.                     String value = "";  
  229.                     //默认第一行为表头,以该行单元格数目为最大数目  
  230.                     if(curRow == 0){  
  231.                         maxRef = ref;  
  232.                     }  
  233.                     //补全一行尾部可能缺失的单元格  
  234.                     if(maxRef != null){  
  235.                         int len = countNullCell(maxRef, ref);  
  236.                         for(int i=0;i<=len;i++){  
  237.                             rowlist.add(curCol, "");  
  238.                             curCol++;  
  239.                         }  
  240.                     }  
  241.                     //拼接一行的数据  
  242.                     for(int i=0;i<rowlist.size();i++){  
  243.                         if(rowlist.get(i).contains(",")){  
  244.                             value += "\""+rowlist.get(i)+"\",";  
  245.                         }else{  
  246.                             value += rowlist.get(i)+",";  
  247.                         }  
  248.                     }  
  249.                     //加换行符  
  250.                     value += "\n";  
  251.                     try {  
  252.                         writer.write(value);  
  253.                     } catch (IOException e) {  
  254.                         e.printStackTrace();  
  255.                     }  
  256.                     curRow++;  
  257.                     //一行的末尾重置一些数据  
  258.                     rowlist.clear();   
  259.                     curCol = 0;   
  260.                     preRef = null;  
  261.                     ref = null;  
  262.                 }   
  263.             }   
  264.         }  
  265.           
  266.         /** 
  267.          * 根据数据类型获取数据 
  268.          * @param value 
  269.          * @param thisStr 
  270.          * @return 
  271.          */  
  272.         public String getDataValue(String value, String thisStr)   
  273.   
  274.         {   
  275.             switch (nextDataType)   
  276.             {   
  277.                 //这几个的顺序不能随便交换,交换了很可能会导致数据错误   
  278.                 case BOOL:   
  279.                 char first = value.charAt(0);   
  280.                 thisStr = first == '0' ? "FALSE" : "TRUE";   
  281.                 break;   
  282.                 case ERROR:   
  283.                 thisStr = "\"ERROR:" + value.toString() + '"';   
  284.                 break;   
  285.                 case FORMULA:   
  286.                 thisStr = '"' + value.toString() + '"';   
  287.                 break;   
  288.                 case INLINESTR:   
  289.                 XSSFRichTextString rtsi = new XSSFRichTextString(value.toString());   
  290.                 thisStr = rtsi.toString();   
  291.                 rtsi = null;   
  292.                 break;   
  293.                 case SSTINDEX:   
  294.                 String sstIndex = value.toString();   
  295.                 thisStr = value.toString();   
  296.                 break;   
  297.                 case NUMBER:   
  298.                 if (formatString != null){   
  299.                     thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString).trim();   
  300.                 }else{  
  301.                     thisStr = value;   
  302.                 }   
  303.                 thisStr = thisStr.replace("_""").trim();   
  304.                 break;   
  305.                 case DATE:   
  306.                     try{  
  307.                         thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString);   
  308.                     }catch(NumberFormatException ex){  
  309.                         thisStr = value.toString();  
  310.                     }  
  311.                 thisStr = thisStr.replace(" """);  
  312.                 break;   
  313.                 default:   
  314.                 thisStr = "";   
  315.                 break;   
  316.             }   
  317.             return thisStr;   
  318.         }   
  319.   
  320.         /** 
  321.          * 获取element的文本数据 
  322.          */  
  323.         public void characters(char[] ch, int start, int length)  
  324.                 throws SAXException {  
  325.             lastContents += new String(ch, start, length);  
  326.         }  
  327.           
  328.         /** 
  329.          * 计算两个单元格之间的单元格数目(同一行) 
  330.          * @param ref 
  331.          * @param preRef 
  332.          * @return 
  333.          */  
  334.         public int countNullCell(String ref, String preRef){  
  335.             //excel2007最大行数是1048576,最大列数是16384,最后一列列名是XFD  
  336.             String xfd = ref.replaceAll("\\d+""");  
  337.             String xfd_1 = preRef.replaceAll("\\d+""");  
  338.               
  339.             xfd = fillChar(xfd, 3'@'true);  
  340.             xfd_1 = fillChar(xfd_1, 3'@'true);  
  341.               
  342.             char[] letter = xfd.toCharArray();  
  343.             char[] letter_1 = xfd_1.toCharArray();  
  344.             int res = (letter[0]-letter_1[0])*26*26 + (letter[1]-letter_1[1])*26 + (letter[2]-letter_1[2]);  
  345.             return res-1;  
  346.         }  
  347.           
  348.         /** 
  349.          * 字符串的填充 
  350.          * @param str 
  351.          * @param len 
  352.          * @param let 
  353.          * @param isPre 
  354.          * @return 
  355.          */  
  356.         String fillChar(String str, int len, char let, boolean isPre){  
  357.             int len_1 = str.length();  
  358.             if(len_1 <len){  
  359.                 if(isPre){  
  360.                     for(int i=0;i<(len-len_1);i++){  
  361.                         str = let+str;  
  362.                     }  
  363.                 }else{  
  364.                     for(int i=0;i<(len-len_1);i++){  
  365.                         str = str+let;  
  366.                     }  
  367.                 }  
  368.             }  
  369.             return str;  
  370.         }  
  371.     }  
  372.       
  373.     static BufferedWriter writer = null;  
  374.   
  375.     public static void main(String[] args) throws Exception {  
  376.         ExampleEventUserModel example = new ExampleEventUserModel();  
  377.         String str = "Book1";  
  378.         String filename = "D:\\"+str+".xlsx ";  
  379.         System.out.println("-- 程序开始 --");  
  380.         long time_1 = System.currentTimeMillis();  
  381.         try{  
  382.             writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("D:\\"+str+".csv")));  
  383.             example.processOneSheet(filename);  
  384.         }finally{  
  385.             writer.close();  
  386.         }  
  387.         long time_2 = System.currentTimeMillis();  
  388.         System.out.println("-- 程序结束 --");  
  389.         System.out.println("-- 耗时 --"+(time_2 - time_1)+"ms");  
  390.     }  
  391.   
  392.   
  393. }  

 

最后输出结果:

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

猜你喜欢

转载自blog.csdn.net/dufufd/article/details/80009436