Apache POI 是創(chuàng)建和維護操作各種符合Office Open XML(OOXML)標準和微軟的OLE 2復合文檔格式(OLE2)的Java API。用它可以使用Java讀取和創(chuàng)建,修改MS Excel文件.而且,還可以使用Java讀取和創(chuàng)建MS Word和MSPowerPoint文件。Apache POI 提供Java操作Excel解決方案(適用于Excel97-2008)虚循。
前言:依賴或下載
<!-- poi 依賴 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.16</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.16</version>
</dependency>
或者在poi.apache.org下載jar包
本篇文章主要是演示Excel文檔的讀取處理猎物。
使用POI解析Excel有兩個需要注意的關(guān)鍵點:
- 遍歷方式的區(qū)別(PhysicalNumber和Number)
- 單元格不同格式的讀取
下面單獨說明一下秆吵。
POI遍歷方式的區(qū)別(PhysicalNumber和Number)
Excel文檔結(jié)構(gòu)分為整個文檔(Workbook)淮椰、分頁(Sheet)、行(Row)纳寂、單元格(Cell)四個類來描述主穗,而遍歷整個Excel也是通過層層遍歷到單元格的方式,但是方式略有不同烈疚。如POI提供了獲取行數(shù)的方法為getPhysicalNumberOfRows黔牵,而這里physical的含義就是去除了空行的數(shù)量聪轿,因此只有保證沒有空行的情況下才能使用這個數(shù)量來遍歷全部行爷肝。去除空行的遍歷:
//行數(shù)
int rowNum=sheet.getPhysicalNumberOfRows();
if(rowNum<1) continue;
// 從第2(r=1)行取出,第一行默認為標題行
rowNum+=1;
//遍歷
for(int r=1;r<rowNum;r++)
{
Row row=sheet.getRow(r);
//其他處理
}
但是陆错,在下一層即遍歷Cell單元格的時候如果還使用類似的getPhysicalNumberOfCells來獲取數(shù)量遍歷則可能出現(xiàn)錯誤灯抛,因為獲取到的數(shù)量是去除了空格的單元格數(shù)量,而單元格的內(nèi)容很可能是空的音瓷,所以這里就要使用一種可以遍歷全部單元格的遍歷方式:
// 循環(huán)取出每行的列數(shù)对嚼,其實是最后一列的列數(shù)
int cellNum=row.getLastCellNum();
// 遍歷
for(int c=0;c<cellNum;c++)
{
// 取出每列值
Cell cell=row.getCell(c);
//其他處理
}
單元格不同格式的讀取
POI解析Cell的類型分為了字符串、數(shù)字绳慎、公式以及布爾四種類型:
- 其中纵竖,字符串通過getRechStringCellValue().toString()來獲取即可;
- 而數(shù)字和公式在Excel中實際儲存的都是Double類型杏愤;
- 布爾類型直接通過getBooleanCellValue()獲取靡砌。
- 比較特殊是日期類型,日期類型實際cellType是數(shù)字珊楼,但是直接讀取數(shù)字則不是想要的格式通殃。為此,POI提供了一個方法(org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell))來判斷是否為日期厕宗,如果為日期格式則使用cell的getDateCellValue方法獲取就可以得到Excel里格式化后的日期內(nèi)容画舌。
工具使用代碼示例
(我這里的業(yè)務(wù)要求是只取部分需要的列里的值,而非全部列里的值導入已慢。)
package pb.utils;
import org.apache.poi.ss.usermodel.*;
import java.io.File;
import java.util.*;
/**
* Excel導入讀取
*/
public class ExcelUtils
{
/**
* 動態(tài)解析Excel文件曲聂,并返回指定的對象列表
*
* @param excelFile
* @param patternKey 匹配使用的主鍵名
* @param pattern 全部匹配值
* @param columnMap 需要獲取的標題與字段名的對應(yīng),如顯示名稱->name佑惠;包含主鍵列
* @return
*/
public static Map<String,LinkedHashMap<String,Object>> resolveExcel(File excelFile,String patternKey,Set<String> pattern,Map<String,String> columnMap) throws Exception
{
//記錄結(jié)果
Map<String,LinkedHashMap<String,Object>> result=new HashMap<>();
//載入文件
Workbook workbook=WorkbookFactory.create(excelFile);
//記錄當前標題與列位置的映射
Map<String,String> titleMap=new LinkedHashMap<>();
//遍歷所有Sheet頁
int pageNum=workbook.getNumberOfSheets();
for(int page=0;page<pageNum;page++)
{
//獲取Sheet
Sheet sheet=workbook.getSheetAt(page);
//遍歷全部非空行
Iterator<Row> rowItr=sheet.rowIterator();
int rowNum=0;
while(rowItr.hasNext())
{
//獲取行
Row row=rowItr.next();
//記錄數(shù)據(jù)
LinkedHashMap<String,Object> map=new LinkedHashMap<>();
//遍歷本行全部列(包括空列)
int colNum=row.getLastCellNum();
for(int index=0;index<colNum;index++)
{
//獲取單元格
Cell cell=row.getCell(index);
//獲取單元格內(nèi)容
Object value=getCellValue(cell);
//第一行為標題行句葵,記錄標題和位置索引的映射
if(rowNum==0)
{
String title=value+"";
if(columnMap.containsKey(title))
titleMap.put(index+"",title);
}
//非標題行厕鹃,根據(jù)指定columnMap和PatternKey查找記錄
else
{
//需要的內(nèi)容
if(!titleMap.containsKey(index+"")) continue;
map.put(columnMap.get(titleMap.get(index+"")),value);
}
}
//驗證是否記錄此行
if(pattern.contains(map.get(patternKey)+""))
{
result.put(map.get(patternKey)+"",map);
}
rowNum++;
}
}
return result;
}
/**
* 依據(jù)Excel中Cell類型讀取不同的值
*
* @param cell
* @return String/Double/Date/boolean/null
*/
public static Object getCellValue(Cell cell)
{
Object result=null;
//獲取類型
int type=cell.getCellType();
//根據(jù)不同類型處理
switch(type)
{
//字符串
case Cell.CELL_TYPE_STRING:
{
result=cell.getRichStringCellValue().toString().trim();
break;
}
//數(shù)字(Double)
case Cell.CELL_TYPE_NUMERIC:
//公式(Double)
case Cell.CELL_TYPE_FORMULA:
{
//日期類型判斷
//日期類型
if(org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell))
{
result=cell.getDateCellValue();
}
//普通數(shù)字
else
{
result=new Double(cell.getNumericCellValue());
}
break;
}
//布爾
case Cell.CELL_TYPE_BOOLEAN:
{
result=cell.getBooleanCellValue();
break;
}
//其他
default:
}
return result;
}
}