POI对Excel自定义日期格式的读取

Posted 史红星

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POI对Excel自定义日期格式的读取相关的知识,希望对你有一定的参考价值。

cell.getCellStyle().getDataFormat();根据这个值进行时间、日期格式的判断;

POI读取出来的结果也是有些变化的;需要在实际项目中进行确认;

时间格式的遍历:

 /**
     * 处理数据类型
     * 
     * @param attributes
     */
    public void setNextDataType(Attributes attributes) {
        nextDataType = CellDataType.NUMBER;
        formatIndex = -1;
        formatString = null;
        String cellType = attributes.getValue("t");
        String cellStyleStr = attributes.getValue("s");
        String columData = attributes.getValue("r");
        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 = this.stylesTable.getStyleAt(styleIndex);
            formatIndex = style.getDataFormat();
            formatString = style.getDataFormatString();
            short format=this.formatIndex;
             if (format == 14 || format == 31 || format == 57 || 
                        format == 58 || (176 <= format && format <= 178)
                        || (182 <= format && format <= 196) || 
                        (210 <= format && format <= 213) || (208 == format)) 
                { // 日期
                    this.formatString = "yyyy-MM-dd";
                    nextDataType = CellDataType.NUMBER;
                } else if (format == 20 || format == 32 || format == 183 || (200 <= format && format <= 209)) { // 时间
                    this.formatString = "HH:mm";
                    nextDataType = CellDataType.NUMBER;
                }
             if (this.formatString == null)
                {
                     nextDataType = CellDataType.NULL;
                     formatString = BuiltinFormats.getBuiltinFormat(formatIndex);
                }
        }
    }

 

 

package com.beyondsoft.util.base;

import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
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.ReadOnlySharedStringsTable;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
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.beyondsoft.util.base.XLSXCovertCSVReader2.xssfDataType;

/**
 * 抽象Excel2007读取器,excel2007的底层数据结构是xml文件,采用SAX的事件驱动的方法解析
 * xml,需要继承DefaultHandler,在遇到文件内容时,事件会触发,这种做法可以大大降低
 * 内存的耗费,特别使用于大数据量的文件。
* 作者:史红星 *
*/ public class Excel2007Reader extends DefaultHandler { //共享字符串表 private ReadOnlySharedStringsTable sharedStringsTable; //上一次的内容 private String lastContents; private boolean nextIsString; // Set when V start element is seen private boolean vIsOpen; private int sheetIndex = -1; private List<String> rowlist = new ArrayList<String>(); //当前行 private int curRow = 0; //当前列 private int curCol = 0; private int preCol = 0; //上一列列索引 //日期标志 private boolean dateFlag; //数字标志 private boolean numberFlag; /** * T元素标识 */ private boolean isTElement; /** * 单元格数据类型,默认为字符串类型 */ private CellDataType nextDataType = CellDataType.SSTINDEX; private final DataFormatter formatter = new DataFormatter(); // 定义前一个元素和当前元素的位置,用来计算其中空的单元格数量,如A6和A8等 private String preRef = null, ref = null; // 定义该文档一行最大的单元格数,用来补全一行最后可能缺失的单元格 private String maxRef = null; // 定义该文档一行最小单元格,用来补全一行最后可能缺失的单元格 private Integer minIndex = null; private short formatIndex; private String formatString; //首列是否为空 private boolean isRowStartNull; /** * 单元格 */ private StylesTable stylesTable; //填充字符串 private static final String CELL_FILL_STR = "@"; //列的最大位数 private static final int MAX_CELL_BIT = 3; private IExcelRowReader rowReader; public void setRowReader(IExcelRowReader rowReader){ this.rowReader = rowReader; } /**只遍历一个电子表格,其中sheetId为要遍历的sheet索引,从1开始,1-3 * @param filename * @param sheetId * @throws Exception */ public void processOneSheet(String filename,int sheetId) throws Exception { OPCPackage pkg = OPCPackage.open(filename); XSSFReader r = new XSSFReader(pkg); this.sharedStringsTable = new ReadOnlySharedStringsTable(pkg); this.stylesTable = r.getStylesTable(); XMLReader parser = fetchSheetParser(this.sharedStringsTable); // 根据 rId# 或 rSheet# 查找sheet InputStream sheet2 = r.getSheet("rId"+sheetId); sheetIndex++; InputSource sheetSource = new InputSource(sheet2); parser.parse(sheetSource); sheet2.close(); } /** * 遍历工作簿中所有的电子表格 * @param filename * @throws Exception */ public void process(String filename) throws Exception { OPCPackage pkg = OPCPackage.open(filename); XSSFReader r = new XSSFReader(pkg); ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(pkg); this.stylesTable = r.getStylesTable(); XMLReader parser = fetchSheetParser(this.sharedStringsTable); Iterator<InputStream> sheets = r.getSheetsData(); while (sheets.hasNext()) { curRow = 0; sheetIndex++; InputStream sheet = sheets.next(); InputSource sheetSource = new InputSource(sheet); parser.parse(sheetSource); sheet.close(); } } /** * 遍历工作簿中所有的电子表格 * @param filename * @throws Exception */ public void process(InputStream filename) throws Exception { OPCPackage pkg = OPCPackage.open(filename); XSSFReader r = new XSSFReader(pkg); this.stylesTable = r.getStylesTable(); //this.sharedStringsTable = new ReadOnlySharedStringsTable(pkg); ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(pkg); XMLReader parser = fetchSheetParser(strings); Iterator<InputStream> sheets = (XSSFReader.SheetIterator)r.getSheetsData(); while (sheets.hasNext()) { curRow = 0; sheetIndex++; InputStream sheet = sheets.next(); InputSource sheetSource = new InputSource(sheet); parser.parse(sheetSource); sheet.close(); } } public XMLReader fetchSheetParser(ReadOnlySharedStringsTable sharedStringsTable) throws SAXException { XMLReader parser = XMLReaderFactory .createXMLReader("org.apache.xerces.parsers.SAXParser"); this.sharedStringsTable = sharedStringsTable; parser.setContentHandler(this); return parser; } //得到列索引,每一列c元素的r属性构成为字母加数字的形式,字母组合为列索引,数字组合为行索引, //如AB45,表示为第(A-A+1)*26+(B-A+1)*26列,45行 public int getRowIndex(String rowStr){ rowStr = rowStr.replaceAll("[^A-Z]", ""); byte[] rowAbc = rowStr.getBytes(); int len = rowAbc.length; float num = 0; for (int i=0;i<len;i++){ num += (rowAbc[i]-\'A\'+1)*Math.pow(26,len-i-1 ); } return (int) num; } /** * 单元格中的数据可能的数据类型 */ enum CellDataType { BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER, NULL,DATE } /** * 处理数据类型 * * @param attributes */ public void setNextDataType(Attributes attributes) { nextDataType = CellDataType.NUMBER; formatIndex = -1; formatString = null; String cellType = attributes.getValue("t"); String cellStyleStr = attributes.getValue("s"); String columData = attributes.getValue("r"); 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 = this.stylesTable.getStyleAt(styleIndex); formatIndex = style.getDataFormat(); formatString = style.getDataFormatString(); short format=this.formatIndex; if (format == 14 || format == 31 || format == 57 || format == 58 || (176 <= format && format <= 178) || (182 <= format && format <= 196) || (210 <= format && format <= 213) || (208 == format)) { // 日期 this.formatString = "yyyy-MM-dd"; nextDataType = CellDataType.NUMBER; } else if (format == 20 || format == 32 || format == 183 || (200 <= format && format <= 209)) { // 时间 this.formatString = "HH:mm"; nextDataType = CellDataType.NUMBER; } if (this.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(sharedStringsTable.getEntryAt(idx)); thisStr = rtss.toString(); rtss = null; } catch (NumberFormatException ex) { thisStr = value.toString(); } break; case NUMBER: String n = value.toString(); // 判断是否是日期格式 if (HSSFDateUtil.isADateFormat(this.formatIndex, n)) { Double d = Double.parseDouble(n); Date date = HSSFDateUtil.getJavaDate(d); thisStr = formateDateToString(date); } else if (this.formatString != null) { thisStr = this.formatter.formatRawCellContents( Double.parseDouble(n), this.formatIndex, this.formatString); } else { thisStr = n; } break; default: thisStr = " "; break; } return thisStr; } /** * 字符串的填充 * * @param str * @param len * @param let * @param isPre * @return */ String fillChar(String str, int len, char let, boolean isPre) { int len_1 = str.length(); if (len_1 < len) { if (isPre) { for (int i = 0; i < (len - len_1); i++) { str = let + str; } } else { for (int i = 0; i < (len - len_1); i++) { str = str + let; } } } return str; } public static void main(String[] args) { String ref="C1"; String preRef="A1"; Excel2007Reader Excel2007Reader=new Excel2007Reader(); System.out.println(Excel2007Reader.countNullCell(ref,preRef)); } /** * 计算两个单元格之间的单元格数目(同一行) * * @param ref * @param preRef * @return */ public int countNullCell(String ref, String preRef) { // excel2007最大行数是1048576,最大列数是16384,最后一列列名是XFD String xfd = ref.replaceAll("\\\\d+", ""); String xfd_1 = preRef.replaceAll("\\\\d+", ""); xfd = fillChar(xfd, 3, \'@\', true); xfd_1 = fillChar(xfd_1, 3, \'@\', true); char[] letter = xfd.toCharArray(); char[] letter_1 = xfd_1.toCharArray(); int res = (letter[0] - letter_1[0]) * 26 * 26 + (letter[1] - letter_1[1]) * 26 + (letter[2] - letter_1[2]); return res; } /** * 填充空白单元格 * * @param curCoordinate * @param preCoordinate */ private void fillBlackCell(String curCoordinate, String preCoordinate, boolean isEnd) { if (!curCoordinate.equals(preCoordinate)) { int len = calNullCellCount(curCoordinate, preCoordinate, isEnd); // for (int i = 0; i < len; i++) { // rowCellList.add(curCell++, ""); // } } } private String formateDateToString(Date date) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 格式化日期 return sdf.format(date); } /** * 计算当前单元格和前一个单元格之间的空白单元格数量 * 如果是尾部则不减1 * * @param curCoordinate * @param preCoordinate * @return */ private int calNullCellCount(String curCoordinate, String preCoordinate, boolean isEnd) { // excel2007最大行数是1048576,最大列数是16384,最后一列列名是XFD String curCellCoordinate = curCoordinate.replaceAll("\\\\d+", ""); String preCellCoordinate = preCoordinate.replaceAll("\\\\d+", ""); curCellCoordinate = fillChar(curCellCoordinate, MAX_CELL_BIT, CELL_FILL_STR); preCellCoordinate = fillChar(preCellCoordinate, MAX_CELL_BIT, CELL_FILL_STR); char[] cur = curCellCoordinate.toCharArray(); char[] pre = preCellCoordinate.toCharArray(); int len = (cur[0] - pre[0]) * 26 * 26 + (cur[1] - pre[1]) * 26 + (cur[2] - pre[2]); if (!isEnd) { len = len - 1; } return len; } /** * 将不足指定位数的字符串补全,高位补上指定字符串 * * @param cellCoordinate * @param maxLen * @param fillChar * @return */ private String fillChar(String cellCoordinate, int maxLen, String fillChar) { int coordinateLen = cellCoordinate.length(); if (coordinateLen < maxLen) { for (int i = 0; i < (maxLen - coordinateLen); i++) { cellCoordinate = fillChar + cellCoordinate; } } return cellCoordinate; } public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { if ("inlineStr".equals(name) || "v".equals(name)) { vIsOpen = true; // Clear contents cache lastContents = ""; } // c => cell else if ("c".equals(name)) { // 前一个单元格的位置 // 当前单元格的位置 ref = attributes.getValue("r");// 设定单元格类型 this.setNextDataType(attributes); // Figure out if the value is an index in the SST String cellType = attributes.getValue("t"); if (cellType != null && cellType.equals("s")) { nextIsString = true; } else { nextIsString = false; } } //当元素为t时 if("t".equals(name)){ isTElement = true; } else { isTElement = false; } // 置空 lastContents = ""; } public void endElement(String uri, String localName, String name) throws SAXException { if(isTElement){ String value = lastContents.trim(); rowlist.add(curCol, value); curCol++; isTElement = false; if(ref!=null&&preRef!=null) { preRef = ref; } // v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引 // 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符 } else if ("v".equals(name)) { // v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引 String value = this.getDataValue(lastContents.trim(), ""); if (preRef==null) { if(this.maxRef!=null&&this.getRowIndex(maxRef)>0) { char index_row = (char) (\'A\'+minIndex); preRef=""+index_row+(curRow+1); if(!ref.equals(preRef)) { // 补全单元格之间的空单元格 int len = calNullCellCount(ref, preRef,false); for (int i = 0; i <=len; i++) { rowlist.add(curCol, ""); curCol++; } } } } else { if(!ref.equals(preRef)){ int len = calNullCellCount(ref, preRef,false); for (int i = 0; i <len; i++) { rowlist.add(curCol, ""); curCol++; } } } rowlist.add(curCol, value); curCol++; preRef = ref; }else { //如果标签名称为 row ,这说明已到行尾,调用 optRows() 方法 if (name.equals("row")) { // 默认第一行为表头,以该行单元格数目为最大数目 if (curRow == 0) { maxRef = ref; minIndex=this.getRowIndex(ref)-rowlist.size(); } // 补全一行尾部可能缺失的单元格 if (maxRef != null) { int len = calNullCellCount(maxRef, ref,true); for (int i = 0; i <len; i++) { rowlist.add(curCol, ""); curCol++; } } rowReader.getRows(sheetIndex,curRow,rowlist); rowlist.clear(); curRow++; curCol = 0; //preCol = 0; preRef = null; ref = null; //isRowStartNull=false; } } } @Override public void characters(char[] ch, int start, int length) throws SAXException { // 得到单元格内容的值 if (this.vIsOpen) { lastContents += new String(ch, start, length); } } }

 

 

 


以上是关于POI对Excel自定义日期格式的读取的主要内容,如果未能解决你的问题,请参考以下文章

java中使用poi导入导出excel文件_并自定义日期格式

使用POI读取xlsx文件,包含对excel中自定义时间格式的处理

如何用Apache POI读取Excel的单元格自定义名称的值

java poi读取excel日期格式数据

poi读取Excel时,如果单元格设置的是数字格式,如何解决整数与小数问题

POI 读取 excel日期格式问题