POI是Apache软件基金会的开放源码函式库,提供API给Java程序对Microsoft Office格式档案读和写的功能。
本文只针对POI实现对Excel的读写功能给出一些指导,以及我在开发过程中踩过的“坑”,以供大家研究学习。
需要的maven依赖包:
1 2 3 4 5 6 7 8 9 10 |
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId><!– Excel2003 –> <version>3.16</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId><!– Excel2007+ –> <version>3.16</version> </dependency> |
一、首先要讲的是如何读取excel
这里有一点要非常注意,就是兼容性问题;Excel有两种文件格式,.xls和.xlsx。
获取文件:
1 |
File file = new File(path);//path是excel路径 |
获得文件流:
1 |
FileInputStream fis = new FileInputStream(file); |
获取工作簿:
1 |
WorkBook wb = WorkBookFactory.create(fis); |
这里WorkBook要考虑兼容性问题:
以xls结尾的应是HSSFWorkBook,以xlsx结尾的应是XSSFWorkBook,
这里可以用以下方法进行兼容性判断:
1 2 3 4 5 6 |
Workbook workBook = null; if (wb instanceof XSSFWorkbook){ workBook = (XSSFWorkbook)wb; } else if (wb instanceof HSSFWorkbook){ workBook = (HSSFWorkbook)wb; } |
获取sheet,两种方式:
1 2 |
Sheet sheet = workBook.getSheetAt(index);//获取编号为index的sheet,index从0开始计数 Sheet sheet = workBook.getSheet(name);//获取名为name的sheet |
获取行对象:
1 |
Row row = sheet.getRow(i);//获取第i行的行对象 |
若要获取第一行表头信息则可直接sheet.getRow(0),这里还可以通过row0.getLastCellNum();获取到行数据的长度(这里可能会获取到大于有效长度的数值,因为编辑的时候可能会保留了空白单元格,但是其单元格格式还保留着,这些单元格也会被识别出来,因此后面获取数据的时候,需要做判空处理),然后可以遍历获取到每个单元格的数据;
当然,也可以通过sheet0.getPhysicalNumberOfRows();获取到有效行数(这里也可能会获取到大于有效长度的数值,原因和6相同,因此获取值的时候也需要做判空处理),然后再遍历获取到所有的行数据;
获取单元格数据:
1 |
Cell cell = row.getCell(i);//获取第i个单元格,i从0开始计数 |
比如要获取到第一列单元格:
1 2 3 4 5 |
int rowNum = sheet.getPhysicalNumberOfRows(); for (int i = 1 ; i < rowNum ; i++) { Row row = sheet.getRow(i); Cell cell = row.getCell(0);//获取第一列单元格 } |
获取值:
这里要考虑到单元格类型,包括字符型,布尔型,数值型(包含日期类型和纯数字类型)和公式类型,另外还可能出现空值或者错误的类型,其中公式类型必须做判断,不然取出来只是计算公式,而不是真正的数值;
可以通过cell.getCellType();获取到单元格的类型,再利用以下代码来进行取值(若直接获取可能会发生类型不一致的错误):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
switch (cell.getCellType()) { case XSSFCell.CELL_TYPE_NUMERIC: // 数值型 if (HSSFDateUtil.isCellDateFormatted(cell)) { //时间类型 SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); stringValue = sdf.format(cell.getDateCellValue()); } else { // 纯数字 double value = cell.getNumericCellValue(); CellStyle style1 = cell.getCellStyle(); DecimalFormat format = new DecimalFormat(); String temp = style1.getDataFormatString(); if (temp.equals("General")) {// 单元格设置成常规 format.applyPattern("#"); } stringValue = format.format(value); } break; case HSSFCell.CELL_TYPE_STRING: // 字符串型 stringValue = cell.getStringCellValue(); break; case HSSFCell.CELL_TYPE_BOOLEAN: // 布尔 stringValue = " " + cell.getBooleanCellValue(); break; case HSSFCell.CELL_TYPE_BLANK: // 空值 stringValue = ""; break; case HSSFCell.CELL_TYPE_ERROR: // 故障 stringValue = ""; break; case HSSFCell.CELL_TYPE_FORMULA: // 公式型 try { CellValue cellValue; cellValue = evaluator.evaluate(cell); switch (cellValue.getCellType()) { // 判断公式类型 case Cell.CELL_TYPE_BOOLEAN: stringValue = String.valueOf(cellValue.getBooleanValue()); break; case Cell.CELL_TYPE_NUMERIC: if (DateUtil.isCellDateFormatted(cell)) { // 处理日期 SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); stringValue = sdf.format(cell.getDateCellValue()); } else { double value = cell.getNumericCellValue(); CellStyle style1 = cell.getCellStyle(); DecimalFormat format = new DecimalFormat(); String temp = style1.getDataFormatString(); // 单元格设置成常规 if (temp.equals("General")) { format.applyPattern("#"); } stringValue = format.format(value); } break; case Cell.CELL_TYPE_STRING: stringValue = cellValue.getStringValue(); break; case Cell.CELL_TYPE_BLANK: stringValue = ""; break; case Cell.CELL_TYPE_ERROR: stringValue = ""; break; case Cell.CELL_TYPE_FORMULA: stringValue = ""; break; } } catch (Exception e) { cell.getCellFormula(); } break; default: stringValue = cell.getStringCellValue().toString(); break; } |
二、写入数据
获取文件:
1 2 3 4 |
File file = new File(path);//path是excel路径,若文件不存在,则新建文件: if (!file.exist()) { file.createNewFile(); } |
获得文件流:
1 |
FileInputStream fis = new FileInputStream(file); |
获取工作簿:
1 |
WorkBook wb = WorkBookFactory.create(fis); |
这里WorkBook要考虑兼容性问题:
以xls结尾的应是HSSFWorkBook,以xlsx结尾的应是XSSFWorkBook,
这里可以用以下方法进行兼容性判断:
1 2 3 4 5 6 |
Workbook workBook = null; if (wb instanceof XSSFWorkbook) { workBook = (XSSFWorkbook) wb; } else if (wb instanceof HSSFWorkbook) { workBook = (HSSFWorkbook) wb; } |
获取sheet,三种方式:
1 2 |
Sheet sheet = workBook.getSheetAt(index);//获取编号为index的sheet,index从0开始计数; Sheet sheet = workBook.getSheet(sheetName);//获取名为sheetName的sheet; |
若是新建文件,则需要新建sheet:
1 |
Sheet sheet = workBook.createSheet(sheetName);//创建名为sheetName的sheet |
获取行对象:
1 |
Row row = sheet.getRow(i);//获取第i行的行对象 |
如果没有row,则需要新建row
1 |
Row row = sheet.createRow(rowNum);//创建一个Row,rowNum为行数,从0开始计数 |
若要获取第一行表头信息则可直接sheet.getRow(0),这里还可以通过row0.getLastCellNum();获取到行数据的长度(这里可能会获取到大于有效长度的数值,因为编辑的时候可能会保留了空白单元格,但是其单元格格式还保留着,这些单元格也会被识别出来,因此后面获取数据的时候,需要做判空处理),然后可以遍历获取到每个单元格的数据;
当然,也可以通过sheet0.getPhysicalNumberOfRows();获取到有效行数(这里也可能会获取到大于有效长度的数值,原因和6相同,因此获取值的时候也需要做判空处理),然后再遍历获取到所有的行数据;
获取单元格:
1 2 |
Cell cell = row.getCell(i);//获取第i个单元格,若没有cell则需新建: Cell cell = row.createCell(cellNum);//新建一个单元格,位置为第cellNum个,cellNum从0开始计数; |
写入数据:
写入数据也要注意数据类型:
1 |
cell.setCellValue(T t);//写入数据到cell, |
如果数据类型对应不上的话,可能会导致数据显示和公式计算结果出错!
数据写完之后,需要刷新公式计算的值,不然公式类型的单元格的值不会刷新:
1 |
workBook.setForceFormulaRecalculation(true);//强制刷新公式的值; |