一、使用poi解析excel文档

注:全部采用poi接口进行解析,不需要区分xls、xlsx格式,不需要判断文档类型。
poi中的日期格式判断仅支持欧美日期习惯,对国内的日期格式并不支持判断,怎么办?所以通过日期格式判断是极其重要的手段,因为日期在excel中也是double类型的数值,所以靠类型判断是极不可靠的,但是有几种常用的日期格式(比如:yyyy-mm-dd,yy-mm-dd等)还是可以通过类型进行判断,因为它们的类型在excel中属于保留值,这点很重要,毕竟office文档想要正确显示出日期类型也是需要通过类型进行判断的。

1、打开excel文件

 /**
* 解析excel文档(支持xls、xlsx格式)
* @param path - 文件路径
* @param formula - 是否获取公式结果
* @param sdf - 日期格式
* @return List - 结果表
* @throws Exception - 打开文件失败
*/
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));//解析sheet表
}
}
else
{
throw new Exception("文件不存在");
}
return list;
}

2、解析sheet表

/**
* 获取sheet表内容
* @param wb - 文档
* @param sheetNum - 打开那张sheet表
* @param formula - 是否获得公式结果
* @param sdf - 日期格式
* @return Map - 结果表
*/
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、解析单元格内容

 /**
* 从sheet表中获取每行每列的值
* @param sheet - sheet表
* @param formula - 是否获取公式结果
* @param sdf - 日期格式
* @return Map - 结果表
*/
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、判断单元格类型并获取内容

 /**
* 判断数值类型自动解析日期格式等其他特殊类型
*
* @param data - 存放数据
* @param cell - 单元格
* @param sdf - 日期格式
* @return String - 结果
*/
private String parseDate(Cell cell, SimpleDateFormat sdf)
{
System.out.println("是否是有效的日期格式:"+DateUtil.isCellDateFormatted(cell));
//poi的日期判断仅适用于欧美日期格式,对中文日期不支持,另外增加两个方法判断中文格式日期
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); }

 /**
* 获取单元格内容
* @param cell - 单元格
* @param sdf - 日期格式
* @param formula - 是否得出公式结果
* @return String - 单元格内容
*/
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: // 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、解析数值类型中的日期:

 /**
* 判断数值类型自动解析日期格式等其他特殊类型
*
* @param data - 存放数据
* @param cell - 单元格
* @param sdf - 日期格式
* @return String - 结果
*/
private String parseDate(Cell cell, SimpleDateFormat sdf)
{
System.out.println("是否是有效的日期格式:"+DateUtil.isCellDateFormatted(cell));
//poi的日期判断仅适用于欧美日期格式,对中文日期不支持,另外增加两个方法判断中文格式日期
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;"> /**
* 是否是日期格式保留字段
* @return boolean<ul><li>true - 是保留字段</li><li>false - 不是</li></ul>
*/
private boolean isReserved(short reserv)
{
if(reserv>=27&&reserv<=31)
{
return true;
}
return false;
}
/**
* 判断是否是中文日期格式
* @param isNotDate
* @return boolean<ul><li>true - 是日期格式</li><li>false - 不是</li></ul>
*/
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;"> /**
* 解析公式
*
* @param data - 存放数据
* @param cell - 单元格
* @param formula - 是否计算公式结果
* @return String - 结果
*/
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中属于保留值,并不会用在其他数值,所以这点事比较放心的。

POI使用:用poi接口不区分xls/xlsx格式解析Excel文档(41种日期格式解析方法,5种公式结果类型解析方法,3种常用数值类型精度控制办法)的更多相关文章

  1. 1、关于python第三方工具操作xls和xlsx格式的excel文档选型的吐血经历

    首先,最近看了python的一本书,其中第7章是关于文章操作的,就计划把python操作excel,word,txt,xml,html,json等格式的文档做个总结,并实现一些功能,但是,第一步就要把 ...

  2. poi做Excel数据驱动,支持.xls和.xlsx格式的excel文档,比起jxl强大不少

    import java.io.FileInputStream;import java.io.InputStream;import java.util.Iterator;import java.util ...

  3. Java使用poi包读取Excel文档

    项目需要解析Excel文档获取数据,就在网上找了一些资料,结合自己这次使用,写下心得: 1.maven项目需加入如下依赖: <dependency> <groupId>org. ...

  4. struts2中利用POI导出Excel文档并下载

    1.项目组负责人让我实现这个接口,因为以前做过类似的,中间并没有遇到什么太困难的事情.其他不说,先上代码: package com.tydic.eshop.action.feedback; impor ...

  5. POI 读取Excel文档中的数据——兼容Excel2003和Excel2007

    Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能. HSSF - 提供读写Microsoft Exce ...

  6. Java之Poi导出Excel文档

    一.Poi简介 在后台管理系统中,我们经常要做的导出操作,通常导出为Excel文档的形式,而Poi则提供了这种需要的支持. 二.Workbook/HSSFWorkbook/XSSFWorkbook 1 ...

  7. Java下使用Apache POI生成具有三级联动下拉列表的Excel文档

    使用Apache POI生成具有三级联动下拉列表的Excel文档: 具体效果图与代码如下文. 先上效果图: 开始贴代码,代码中部分测试数据不影响功能. 第一部分(核心业务处理): 此部分包含几个方面: ...

  8. poi 读取使用 Strict Open XML 保存的 excel 文档

    poi 读取使用 Strict Open XML 保存的 excel 文档 某项目有一个功能需要读取 excel 报表内容,使用poi读取时报错: 具体错误为: org.apache.poi.POIX ...

  9. POI加dom4j将数据库的数据按一定格式生成word文档

    一:需求:将从数据库查处来的数据,生成word文档,并有固定的格式.(dom4j的jar包+poi的jar包) 二:解决:(1)先建立固定格式的word文档(2007版本以上),另存成为xml文件,作 ...

随机推荐

  1. Linux密码保护

    在之前写了Linux密码破解的方法,虽然这样对于忘记密码时很方便,但同时别人也可以很轻易的破解你的Liunx虚拟机,安全问题存在隐患. 下面给出一些Liunx密码的安全防护操作: 1.防止破解root ...

  2. LVS+Keepalived实现DBProxy的高可用

    背景 在上一篇文章美团点评DBProxy读写分离使用说明实现了读写分离,但在最后提了二个问题:一是代理不管MySQL主从的复制状态,二是DBProxy本身是一个单点的存在.对于第一个可以通过自己定义的 ...

  3. [bug] Cannot proceed because system tables used by Event Scheduler were found damaged at server start

    本地:mac 10.12.3  mysql 5.6 远程:linux 7.3    mysql 5.7.18.  (远程数据库yum安装,又5.6升级到5.7) 步骤:从本地数据库导出数据到远程数据库 ...

  4. DirectFB学习笔记一

    本文记录directfb程序的基本操作流程. 1.首先创建一个directfb对象:DirectFBInit(&argc,&argv)初始化然后创建DirectFBCreate(&am ...

  5. 【原创】bootstrap框架的学习 第六课[bootstrap代码]

    Bootstrap 允许您以两种方式显示代码: 第一种是 <code> 标签.如果您想要内联显示代码,那么您应该使用 <code> 标签. 第二种是 <pre> 标 ...

  6. linux下安编译安装redis

    1.先进入要安装到的目录,比如我要把redis安装到/usr/local/redis下,那就先进入/usr/local cd /usr/local 2.然后下载安装包,并解压 wget http:// ...

  7. 详解Java反射机制

    反射是程序在运行状态下,动态的获取某个类的内部信息的一种操作.例如:类名,包名,所有属性的集合,所有方法的集合,构造方法的集合等.该操作发生在程序的运行时状态,所以编译器管不着有关反射的一些代码,通常 ...

  8. [刷题]算法竞赛入门经典(第2版) 5-12/UVa511 - Do You Know the Way to San Jose?

    题意:N张地图,查找某地点在不在某些地图上,若在,使用细节多的地图.使用哪个地图的破要求挺多,细心一点就好. 代码:(Accepted,0.000s) //UVa511 - Do You Know t ...

  9. 如何用php实现简单的文件上传功能?(带图解)

    如图所示:点击浏览出现选择文件的对话框,将所选文件上传到保存文件的文件.  关键点:文件上传的图解: 代码: <!DOCTYPE html> <html> <head&g ...

  10. webpack的多文件打包问题

    1.第三方库如vue,vue-router可以利用webpack中的entry指定vendor:['vue','vue-router']来打包在一个文件中 2.将这些文件单独提取出来,在页面中使用&l ...