1 package com.cib.common.excelUnils;
  2 
  3 import java.io.IOException;
  4 import java.io.InputStream;
  5 import java.util.ArrayList;
  6 import java.util.Iterator;
  7 import java.util.List;
  8 import java.util.regex.Matcher;
  9 import java.util.regex.Pattern;
 10 
 11 import org.apache.commons.lang.StringUtils;
 12 import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
 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.InputSource;
 23 import org.xml.sax.SAXException;
 24 import org.xml.sax.XMLReader;
 25 import org.xml.sax.helpers.DefaultHandler;
 26 import org.xml.sax.helpers.XMLReaderFactory;
 27 
 28 /**
 29  * 名称: ExcelXlsxReader.java<br>
 30  * 描述: <br>
 31  * 类型: JAVA<br>
 32  * 最近修改时间:2017年11月09日 上午10:00:52<br>
 33  * 
 34  * @since 2017年11月09日
 35  * @author “小涛”
 36  */
 37 public abstract class ExcelXlsxReader extends DefaultHandler {
 38 
 39     /**
 40      * 共享字符串表
 41      */
 42     private SharedStringsTable sst;
 43 
 44 
 45     /**
 46      * 上一次的内容
 47      */
 48     private String lastContents;
 49 
 50 
 51     /**
 52      * 字符串标识
 53      */
 54     private boolean nextIsString;
 55 
 56 
 57     /**
 58      * 工作表索引
 59      */
 60     private int sheetIndex = -1;
 61 
 62 
 63     /**
 64      * 行集合
 65      */
 66     private List<String> rowlist = new ArrayList<String>();
 67 
 68 
 69     /**
 70      * 当前行
 71      */
 72     private int curRow = 0;
 73 
 74 
 75     /**
 76      * 当前列
 77      */
 78     private int curCol = 0;
 79 
 80 
 81     /**
 82      * T元素标识
 83      */
 84     private boolean isTElement;
 85 
 86 
 87     /**
 88      * 异常信息,如果为空则表示没有异常
 89      */
 90     private String exceptionMessage;
 91 
 92 
 93     /**
 94      * 单元格数据类型,默认为字符串类型
 95      */
 96     private CellDataType nextDataType = CellDataType.SSTINDEX;
 97 
 98 
 99     private final DataFormatter formatter = new DataFormatter();
100 
101 
102     private short formatIndex;
103 
104 
105     private String formatString;
106 
107 
108     // 定义前一个元素和当前元素的位置,用来计算其中空的单元格数量,如A6和A8等
109     private String preRef = null, ref = null;
110 
111 
112     // 定义该文档一行最大的单元格数,用来补全一行最后可能缺失的单元格
113     private String maxRef = null;
114 
115 
116     /**
117      * 单元格
118      */
119     private StylesTable stylesTable;
120 
121 
122     /**
123      * 遍历工作簿中所有的电子表格
124      * 
125      * @param filename
126      * @throws IOException
127      * @throws OpenXML4JException
128      * @throws SAXException
129      * @throws Exception
130      */
131     public void process(String filename) throws IOException, OpenXML4JException, SAXException {
132         OPCPackage pkg = OPCPackage.open(filename);
133         XSSFReader xssfReader = new XSSFReader(pkg);
134         stylesTable = xssfReader.getStylesTable();
135         SharedStringsTable sst = xssfReader.getSharedStringsTable();
136         XMLReader parser = this.fetchSheetParser(sst);
137         Iterator<InputStream> sheets = xssfReader.getSheetsData();
138         while (sheets.hasNext()) {
139             curRow = 0;
140             sheetIndex++;
141             InputStream sheet = sheets.next();
142             InputSource sheetSource = new InputSource(sheet);
143             parser.parse(sheetSource);
144             sheet.close();
145         }
146     }
147 
148 
149     public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {
150         XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
151         this.sst = sst;
152         parser.setContentHandler(this);
153         return parser;
154     }
155 
156 
157     public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
158         // c => 单元格
159         if ("c".equals(name)) {
160             // 前一个单元格的位置
161             if (preRef == null) {
162                 preRef = attributes.getValue("r");
163             } else {
164                 preRef = ref;
165             }
166             // 当前单元格的位置
167             ref = attributes.getValue("r");
168             // 设定单元格类型
169             this.setNextDataType(attributes);
170             // Figure out if the value is an index in the SST
171             String cellType = attributes.getValue("t");
172             if (cellType != null && cellType.equals("s")) {
173                 nextIsString = true;
174             } else {
175                 nextIsString = false;
176             }
177         }
178 
179 
180         // 当元素为t时
181         if ("t".equals(name)) {
182             isTElement = true;
183         } else {
184             isTElement = false;
185         }
186 
187 
188         // 置空
189         lastContents = "";
190     }
191 
192 
193     /**
194      * 单元格中的数据可能的数据类型
195      */
196     enum CellDataType {
197         BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER, DATE, NULL
198     }
199 
200 
201     /**
202      * 处理数据类型
203      * 
204      * @param attributes
205      */
206     public void setNextDataType(Attributes attributes) {
207         nextDataType = CellDataType.NUMBER;
208         formatIndex = -1;
209         formatString = null;
210         String cellType = attributes.getValue("t");
211         String cellStyleStr = attributes.getValue("s");
212         String columData = attributes.getValue("r");
213 
214         if ("b".equals(cellType)) {
215             nextDataType = CellDataType.BOOL;
216         } else if ("e".equals(cellType)) {
217             nextDataType = CellDataType.ERROR;
218         } else if ("inlineStr".equals(cellType)) {
219             nextDataType = CellDataType.INLINESTR;
220         } else if ("s".equals(cellType)) {
221             nextDataType = CellDataType.SSTINDEX;
222         } else if ("str".equals(cellType)) {
223             nextDataType = CellDataType.FORMULA;
224         }
225 
226 
227         if (cellStyleStr != null) {
228             int styleIndex = Integer.parseInt(cellStyleStr);
229             XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);
230             formatIndex = style.getDataFormat();
231             formatString = style.getDataFormatString();
232 
233 
234             if ("m/d/yy" == formatString) {
235                 nextDataType = CellDataType.DATE;
236                 formatString = "yyyy-MM-dd hh:mm:ss";
237             }
238 
239 
240             if (formatString == null) {
241                 nextDataType = CellDataType.NULL;
242                 formatString = BuiltinFormats.getBuiltinFormat(formatIndex);
243             }
244         }
245     }
246 
247 
248     /**
249      * 对解析出来的数据进行类型处理
250      * 
251      * @param value
252      *            单元格的值(这时候是一串数字)
253      * @param thisStr
254      *            一个空字符串
255      * @return
256      */
257     public String getDataValue(String value, String thisStr) {
258         switch (nextDataType) {
259         // 这几个的顺序不能随便交换,交换了很可能会导致数据错误
260         case BOOL:
261             char first = value.charAt(0);
262             thisStr = first == '0' ? "FALSE" : "TRUE";
263             break;
264         case ERROR:
265             thisStr = "\"ERROR:" + value.toString() + '"';
266             break;
267         case FORMULA:
268             thisStr = '"' + value.toString() + '"';
269             break;
270         case INLINESTR:
271             XSSFRichTextString rtsi = new XSSFRichTextString(value.toString());
272 
273             thisStr = rtsi.toString();
274             rtsi = null;
275             break;
276         case SSTINDEX:
277             String sstIndex = value.toString();
278             try {
279                 int idx = Integer.parseInt(sstIndex);
280                 XSSFRichTextString rtss = new XSSFRichTextString(sst.getEntryAt(idx));
281                 thisStr = rtss.toString();
282                 rtss = null;
283             } catch (NumberFormatException ex) {
284                 thisStr = value.toString();
285             }
286             
287             break;
288         case NUMBER:
289             if (formatString != null) {
290                 thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString).trim();
291             } else {
292                 thisStr = value;
293             }
294             thisStr = thisStr.replace("_", "").trim();
295             
296             break;
297         case DATE:
298             //获取的日期是一串数字、需要使用此HasDigit方法来过滤第一行的表头、日期类型
299             if (HasDigit(value)) {
300                 thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString);
301                 // 对日期字符串作特殊处理
302                 thisStr = thisStr.replace(" ", " ");
303                 break;
304             }
305         default:
306             thisStr = "null";
307             break;
308         }
309         return thisStr;
310     }
311 
312 
313     @Override
314     public void endElement(String uri, String localName, String name) throws SAXException {
315         // 根据SST的索引值的到单元格的真正要存储的字符串
316         // 这时characters()方法可能会被调用多次
317         if (nextIsString  &&  StringUtils.isNotEmpty(lastContents) && StringUtils.isNumeric(lastContents)) {
318             int idx = Integer.parseInt(lastContents);
319             lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString();
320         }
321 
322 
323         // t元素也包含字符串
324         if (isTElement) {
325             // 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符
326             String value = lastContents.trim();
327             rowlist.add(curCol, value);
328             curCol++;
329             isTElement = false;
330         } else if ("v".equals(name)) {
331             // v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引
332             String value = this.getDataValue(lastContents.trim(), "");
333             // 补全单元格之间的空单元格
334             if (!ref.equals(preRef)) {
335                 int len = countNullCell(ref, preRef);
336                 for (int i = 0; i < len; i++) {
337                     rowlist.add(curCol, "null");
338                     curCol++;
339                 }
340             }
341             rowlist.add(curCol, value);
342             curCol++;
343         } else {
344             // 如果标签名称为 row ,这说明已到行尾,调用 optRows() 方法
345             if (name.equals("row")) {
346                 // 默认第一行为表头,以该行单元格数目为最大数目
347                 if (curRow == 0) {
348                     maxRef = ref;
349                 }
350                 // 补全一行尾部可能缺失的单元格
351                 if (maxRef != null) {
352                     int len = countNullCell(maxRef, ref);
353                     for (int i = 0; i <= len; i++) {
354                         rowlist.add(curCol, "");
355                         curCol++;
356                     }
357                 }
358                 getRows(sheetIndex, curRow, rowlist);
359                 rowlist.clear();
360                 curRow++;
361                 curCol = 0;
362                 preRef = null;
363                 ref = null;
364             }
365         }
366     }
367 
368 
369     /**
370      * 计算两个单元格之间的单元格数目(同一行)
371      * 此方法用来判断单元格是空的、如不加、则不会读取空的单元格
372      * @param ref
373      * @param preRef
374      * @return
375      */
376     public int countNullCell(String ref, String preRef) {
377         // excel2007最大行数是1048576,最大列数是16384,最后一列列名是XFD
378         String xfd = ref.replaceAll("\\d+", "");
379         String xfd_1 = preRef.replaceAll("\\d+", "");
380 
381         xfd = fillChar(xfd, 3, '@', true);
382         xfd_1 = fillChar(xfd_1, 3, '@', true);
383 
384         char[] letter = xfd.toCharArray();
385         char[] letter_1 = xfd_1.toCharArray();
386         int res = (letter[0] - letter_1[0]) * 26 * 26 + (letter[1] - letter_1[1]) * 26 + (letter[2] - letter_1[2]);
387         return res - 1;
388     }
389 
390 
391     /**
392      * 字符串的填充
393      * 
394      * @param str
395      * @param len
396      * @param let
397      * @param isPre
398      * @return
399      */
400     String fillChar(String str, int len, char let, boolean isPre) {
401         int len_1 = str.length();
402         if (len_1 < len) {
403             if (isPre) {
404                 for (int i = 0; i < (len - len_1); i++) {
405                     str = let + str;
406                 }
407             } else {
408                 for (int i = 0; i < (len - len_1); i++) {
409                     str = str + let;
410                 }
411             }
412         }
413         return str;
414     }
415 
416 
417     @Override
418     public void characters(char[] ch, int start, int length) throws SAXException {
419         // 得到单元格内容的值
420         lastContents += new String(ch, start, length);
421     }
422 
423 
424     /**
425      * @return the exceptionMessage
426      */
427     public String getExceptionMessage() {
428         return exceptionMessage;
429     }
430     
431     /**
432      * 获取行数据回调
433      * 
434      * @param sheetIndex
435      * @param curRow
436      * @param rowList
437      */
438     public abstract void getRows(int sheetIndex, int curRow,
439             List<String> rowList);
440     
441     //判断一个字符串是否含有数字
442     public boolean HasDigit(String content) {
443             boolean flag = false;
444             Pattern p = Pattern.compile("[1-9]\\d*\\.?\\d*");
445             Matcher m = p.matcher(content);
446             if (m.matches()) {
447                 flag = true;
448             }
449             return flag;
450         }
451 
452 }