一、使用poi解析excel文档
注:全部采用poi接口进行解析,不需要区分xls、xlsx格式,不需要判断文档类型。
poi中的日期格式判断仅支持欧美日期习惯,对国内的日期格式并不支持判断,怎么办?所以通过日期格式判断是极其重要的手段,因为日期在excel中也是double类型的数值,所以靠类型判断是极不可靠的,但是有几种常用的日期格式(比如:yyyy-mm-dd,yy-mm-dd等)还是可以通过类型进行判断,因为它们的类型在excel中属于保留值,这点很重要,毕竟office文档想要正确显示出日期类型也是需要通过类型进行判断的。
1、打开excel文件
-
-
-
-
-
-
-
-
- public List<Map> parse(String path, boolean formula, SimpleDateFormat sdf)
- throws Exception
- {
- path = this.getClass().getResource("/").getPath() + path;
- File file = new File(path);
- List<Map>list=null;
- if (file.isFile())
- {
- Workbook wb = createWorkbook(file);
- System.out.println("当前活动sheet" + wb.getActiveSheetIndex());
- System.out.println("当前几个文档"+wb.getNumberOfSheets());
- list=new ArrayList<Map>();
- int max=wb.getNumberOfSheets();
- for(int sheetNum=0;sheetNum<max;sheetNum++){
- list.add(getSheet(wb, sheetNum, formula, sdf));
- }
- }
- else
- {
- throw new Exception("文件不存在");
- }
- return list;
- }
2、解析sheet表
-
-
-
-
-
-
-
-
- public Map<Integer, Map<Integer, String>> getSheet(Workbook wb, int sheetNum, boolean formula, SimpleDateFormat sdf)
- {
- String sheetName = wb.getSheetName(sheetNum);
- System.out.println("打开了sheet表:" + sheetName);
- Sheet sheet = wb.getSheet(sheetName);
- Map<Integer, Map<Integer, String>> map=getRowAndCell(sheet, formula, sdf);
- return map;
-
- }
3、解析单元格内容
-
-
-
-
-
-
-
- public Map<Integer, Map<Integer, String>> getRowAndCell(Sheet sheet, boolean formula, SimpleDateFormat sdf)
- {
- Map<Integer,Map<Integer,String>>rowMap=null;
- int firstRowNum = sheet.getFirstRowNum();
- int lastRowNum = sheet.getLastRowNum();
-
- if(lastRowNum>0){
- rowMap=new HashMap<Integer,Map<Integer,String>>();
-
-
- for (int rowNum = firstRowNum; rowNum <= lastRowNum; rowNum++ )
- {
- Row row = sheet.getRow(rowNum);
- int firstCellNum = row.getFirstCellNum();
- int lastCellNum = row.getLastCellNum();
- Map<Integer,String> cellMap=new HashMap<Integer,String> ();
-
- for (int cellNum = firstCellNum; cellNum < lastCellNum; cellNum++ )
- {
- Cell cell = row.getCell(cellNum);
- int type = cell.getCellType();
- String data=getValue(cell, formula, sdf);
- System.out.println("第" + rowNum + "行,第" + cellNum + "列,类型是" + type +",内容是:"+data);
- cellMap.put(cellNum, data);
- }
- rowMap.put(rowNum, cellMap);
- }
- }
- return rowMap;
- }
4、判断单元格类型并获取内容
-
-
-
-
-
-
-
-
- private String parseDate(Cell cell, SimpleDateFormat sdf)
- {
- System.out.println("是否是有效的日期格式:"+DateUtil.isCellDateFormatted(cell));
-
- if (DateUtil.isCellDateFormatted(cell)||isReserved(cell.getCellStyle().getDataFormat())||isDateFormat(cell.getCellStyle().getDataFormatString()))
- {
- return sdf.format(cell.getDateCellValue());
- }
- System.out.println("格式:"+cell.getCellStyle().getDataFormatString()+",类型"+cell.getCellStyle().getDataFormat());
- Double d=cell.getNumericCellValue();
- if(cell.getCellStyle().getDataFormat()==0)
- {
- DecimalFormat dfs = new DecimalFormat("0");
- return dfs.format(d);
- }
- return String.valueOf(d);
-
- }
-
-
-
-
-
-
-
- private String getValue(Cell cell, boolean formula, SimpleDateFormat sdf)
- {
- String data = null;
- switch (cell.getCellType())
- {
- case Cell.CELL_TYPE_NUMERIC:
- data = parseDate(cell, sdf);
- break;
- case Cell.CELL_TYPE_STRING:
-
- data = cell.getStringCellValue();
- break;
- case Cell.CELL_TYPE_BOOLEAN:
- data = String.valueOf(cell.getBooleanCellValue());
- break;
- case Cell.CELL_TYPE_FORMULA:
-
- data = parseFormula(cell, formula);
- break;
- case Cell.CELL_TYPE_BLANK:
- System.out.println("遇到一个空格");
- data = null;
- break;
- case Cell.CELL_TYPE_ERROR:
- System.out.println("遇到一个错误");
- data = null;
- break;
- default:
- data = null;
- }
- return data;
- }
二、poi的6种基本类型
Cell.CELL_TYPE_NUMERIC: // 数值
Cell.CELL_TYPE_STRING: // 字符串
Cell.CELL_TYPE_BOOLEAN: // Boolean
Cell.CELL_TYPE_FORMULA: // 公式
Cell.CELL_TYPE_BLANK: // 空格
Cell.CELL_TYPE_ERROR:// 错误
三、41种日期格式解析方法
注意:看着好像有几个是重复的,但是它们的日期格式是不一样的(比如yyyy-m-d与yyyy-mm-dd同样都是显示:2015-12-13)
日期表(1-41):
1 2015-12-13
2 2015年12月
3 2015年12月15日
4 十二月十六日
5 二〇一五年十二月
6 二〇一五年十二月十八日
7 12月13日
8 2015-12-13 12:00 AM
9 2015-12-14 0:00
10 15-12-15
11 12-16
12 12-17-15
13 12-18-15
14 19-Dec
15 20-Dec-15
16 21-Dec-15
17 Dec-15
18 December-15
19 D
20 D-15
21 2015年12月26日
22 2015年12月
23 二〇一五年十二月二十七日
24 二〇一五年十二月
25 十二月二十九日
26 12月30日
27 星期四
28 五
29 2016-1-2
30 2016-1-3 12:00 AM
31 2016-1-4 0:00
32 16-1-5
33 1-6
34 1-7-16
35 01-08-16
36 9-Jan
37 10-Jan-16
38 Jan-16
39 January-16
40 J
41 J-16
2、日期对应的类型(0-40对应上面日期表1-41)
序号=类型
0=14,
1=27,
2=31,
3=176,
4=177,
5=178,
6=28,
7=179,
8=22,
9=180,
10=181,
11=30,
12=182,
13=16,
14=15,
15=183,
16=17,
17=184,
18=185,
19=186,
20=187,
21=188,
22=189,
23=190,
24=191,
25=192,
26=193,
27=194,
28=195,
29=196,
30=197,
31=198,
32=199,
33=200,
34=201,
35=202,
36=203,
37=204,
38=205,
39=206,
40=207
3、对应的日期格式(0-40,同上):
0=m/d/yy,
1=reserved-0x1b,
2=reserved-0x1f,
3=[DBNum1][$-804]m"月"d"日",
4=[DBNum1][$-804]yyyy"年"m"月",
5=[DBNum1][$-804]yyyy"年"m"月"d"日",
6=reserved-0x1c,
7=yyyy/m/d\ h:mm\ AM/PM,
8=m/d/yy h:mm,
9=yy/m/d,
10=m/d,
11=reserved-0x1e,
12=mm/dd/yy,
13=d-mmm,
14=d-mmm-yy,
15=dd/mmm/yy,
16=mmm-yy,
17=mmmm/yy,
18=mmmmm,
19=mmmmm/yy,
20=yyyy"年"m"月"d"日";@,
21=yyyy"年"m"月";@,
22=[DBNum1][$-804]yyyy"年"m"月"d"日";@,
23=[DBNum1][$-804]yyyy"年"m"月";@,
24=[DBNum1][$-804]m"月"d"日";@,
25=m"月"d"日";@,
26=[$-804]aaaa;@,
27=[$-804]aaa;@,
28=yyyy/m/d;@,
29=[$-409]yyyy/m/d\ h:mm\ AM/PM;@,
30=yyyy/m/d\ h:mm;@,
31=yy/m/d;@,
32=m/d;@,
33=m/d/yy;@,
34=mm/dd/yy;@,
35=[$-409]d/mmm;@,
36=[$-409]d/mmm/yy;@,
37=[$-409]mmm/yy;@,
38=[$-409]mmmm/yy;@,
39=[$-409]mmmmm;@,
40=[$-409]mmmmm/yy;@
根据上述的格式进行单独判断就可以正确解析所有日期格式。下面是我的实现方式,可能效率不高,如果有其它高效的方法可以提出来,欢迎一起交流
4、解析数值类型中的日期:
-
-
-
-
-
-
-
-
- private String parseDate(Cell cell, SimpleDateFormat sdf)
- {
- System.out.println("是否是有效的日期格式:"+DateUtil.isCellDateFormatted(cell));
-
- if (DateUtil.isCellDateFormatted(cell)||isReserved(cell.getCellStyle().getDataFormat())||isDateFormat(cell.getCellStyle().getDataFormatString()))
- {
- return sdf.format(cell.getDateCellValue());
- }
- System.out.println("格式:"+cell.getCellStyle().getDataFormatString()+",类型"+cell.getCellStyle().getDataFormat());
- Double d=cell.getNumericCellValue();
- if(cell.getCellStyle().getDataFormat()==0)
- {
- DecimalFormat dfs = new DecimalFormat("0");
- return dfs.format(d);
- }
- return String.valueOf(d);
-
- }
- <span style="font-size:18px;">
-
-
-
- private boolean isReserved(short reserv)
- {
- if(reserv>=27&&reserv<=31)
- {
- return true;
- }
- return false;
- }
-
-
-
-
-
- private boolean isDateFormat(String isNotDate)
- {
- if(isNotDate.contains("年")||isNotDate.contains("月")||isNotDate.contains("日"))
- {
- return true;
- }
- else if(isNotDate.contains("aaa;")||isNotDate.contains("AM")||isNotDate.contains("PM"))
- {
- return true;
- }
-
-
- return false;
- }</span>
四、5种公式类型及结果解析方法
1、公式只有一种,结果分为5种
除了基本类型中的空格不可能是结果,其他几种结果都可能是公式计算出来的结果
2、解析5种公式
- <span style="font-size:18px;">
-
-
-
-
-
-
-
- private String parseFormula(Cell cell, boolean formula)
- {
- String data = null;
- if (formula)
- {
- switch (cell.getCachedFormulaResultType())
- {
- case 0:
- if (0 == cell.getCellStyle().getDataFormat())
- {
- DecimalFormat df = new DecimalFormat("0");
- data = df.format(cell.getNumericCellValue());
- }
- else
- {
- data = String.valueOf(cell.getNumericCellValue());
- }
- break;
- case 1:
- data = String.valueOf(cell.getRichStringCellValue());
- break;
- case 4:
- data = String.valueOf(cell.getBooleanCellValue());
- break;
- case 5:
- data = String.valueOf(cell.getErrorCellValue());
- break;
- default:
- data = cell.getCellFormula();
- }
- }
- else
- {
- data = cell.getCellFormula();
- }
- return data;
- }</span>
五、3种数值类型(货币,浮点、整数)精度控制(正确解析整数型数值)
货币等同于浮点数
整数一般用于序号和手机号码,邮编等等整数型数值表示
1、很有意思的是整数型的数据,如果没有设置自定义格式,那么是这样的:
默认格式,类型值
123 类型是 0
18094.75 类型是 2
100.02119422386752 类型是176
99.95066018068103 类型是178
2、如果设置了特殊格式,比如货币类型或者自定义类型,都属于特殊类型:
格式,类型值
_($*#,##0.00_);_($*(#,##0.00);_($*"-"??_);_(@_),类型44
_(*#,##0.00_);_(*(#,##0.00);_(*"-"??_);_(@_),类型43
0.0000_ ,类型208
"¥"#,##0.000;"¥"\-#,##0.000,类型209
总结:可能有人已经看出来了,日期格式中有几种类型还是跟数值类型一样的,怎么办?所以通过日期格式判断是极其重要方法,因为日期在excel中也是double类型的数值,所以靠类型判断是极不可靠的,但是有几种常用的日期格式(比如:yyyy-mm-dd,yy-mm-dd等)还是可以通过类型进行判断,因为它们的类型在excel中属于保留值,并不会用在其他数值,所以这点事比较放心的。