欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

POI学习笔记,Excel解析实现

程序员文章站 2022-06-07 15:22:40
...
最近要完成一个根据模板将Excel数据批量导入数据库的业务
本打算用Kettle,但是客户提供的Excel模板实在太乱,Spoon没办法完成解析,只好打算手动实现

poi框架可以实现对MS Office文档的查看、修改功能,实际Kettle对Excel识别模块也是用poi框架完成的,这里我们只需要引用Excel关联的模块

给出Maven Dependency:
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.9</version>
        </dependency>

其中poi是Office 2003格式的标准库,poi-ooxml是Office 2007的扩展库

对于Excel来说,处理的根节点就是Workbook接口,对应2003(xls)和2007(xlsx)格式分别有两个实现类:HSSFWorkbook和XSSFWorkbook

之后对应的还有HSSFSheet和XSSFSheet、HSSFRow和XSSFRow、HSSFCell和XSSFCell

当然,这几个类分别都实现了Sheet接口、Row接口、Cell接口
所以当不确定文件格式的时候只要在创建Workbook的时候创建对应的Workbook实现类,之后都可以直接使用接口进行通用操作

而创建对应的Workbook实现类也在poi内部有所实现:
Workbook workbook = WorkbookFactory.create(inputStream);


传入的是一个包含文件数据的InputStream。当然create方法在WorkbookFactory中重载了多次,具体可以自行参考源码和API

注意的是WorkbookFactory在create Workbook对象的时候,若传入的文件流根本不是Excel格式的时候会抛出一个RuntimeException的继承类IllegalArgumentException,因为继承自RuntimeException,所以不会要求捕获。如果传入文件接口是开放的时候需要手动去try cache这个Exception

调用WorkbookFactory的create方法生成Workbook方法比手动判断文件后缀更安全有效,毕竟文件可能被修改过后缀名。

-------------------------------------------------------
有效表格范围

poi只能获取大致的有效表格范围,并且只能得到行范围,列范围需要遍历Row对象以后取最大值得到。但是这个范围不一定准确,常常把空白行和列包含进去,poi的判断应该是未被修改过格式且玩诶空值。当然只会多不会少。如果对格式要求比较高可能需要手动去过滤掉空行空列

poi内置有一个类表示表格范围:CellRangeAddress,这个类是用来表示合并单元格的范围的,但是用来表示表格的有效范围也适用
---------------------------------------------------------
合并单元格
合并单元格对象需要在Sheet对象中得到。无法在Cell对象中获取
可以用Sheet接口的getNumMergedRegions()取得数量,再用getMergedRegion(index)方法得到每个合并单元格范围,得到的是CellRangeAddress对象
        List<CellRangeAddress> mergedRegions = new ArrayList<CellRangeAddress>();
        int cellCount = sheet.getNumMergedRegions();
        for (int index=0;index<cellCount;index++){
            mergedRegions.add(sheet.getMergedRegion(index));
        }
        return mergedRegions;


---------------------------------------------------------
合并单元格内容读取
合并单元格内部只有左上角的单元格坐标可以得到合并单元格的值,其他坐标单元格只能得到一个空字符串。因此需要事先遍历合并单元格列表检查当前单元格坐标是否在某个合并单元格内部,然后以合并单元格左上角坐标代替当前坐标读取内容即可

而判断方法在CellRangeAddress内有封装,即isInRange(row,col)
    private Cell checkInMergedRegion(int row,int col){
        for (CellRangeAddress mergedAddress:mergedRegions){
            if (mergedAddress.isInRange(row,col)){
                return getCell(mergedAddress.getFirstRow(),mergedAddress.getFirstColumn());
            }
        }
        return getCell(row, col);
    }

    private Cell getCell(int rowNumber,int colNumber){
        Row row = sheet.getRow(rowNumber);
        return row.getCell(colNumber);
    }


--------------------------------------------------------
读取单元格内容
首先获取Cell的内容类型,分为5种,分别为空白、字符串、数字、公式、布尔。根据不同类型做对应处理。
注意的是公式类型的处理。可以调用cell.getNumericCellValue()方法直接获得公式计算结果。
    private String getCellStringValue(Cell cell) {
        int type = cell.getCellType();
        switch (type) {
            case Cell.CELL_TYPE_BLANK:
            case Cell.CELL_TYPE_STRING:
                return cell.getStringCellValue();
            case Cell.CELL_TYPE_NUMERIC:
            case Cell.CELL_TYPE_FORMULA:
                //取得数值并取两位小数,同时转为字符串
                return roundNumber(cell.getNumericCellValue(), 2);
            case Cell.CELL_TYPE_BOOLEAN:
                return cell.getBooleanCellValue() + "";
            default:
                return "ERROR VALUE";
        }
    }


---------------------------------
百分数的处理
excel中的百分数在poi中并没有做特别的处理,其默认类型是Cell.CELL_TYPE_NUMERIC,所以如果不做特别处理的话取出来的将是这个百分数对应的小数的值。如果需要对百分数做特别处理的情况(如将百分数作为字符串提取出来)就必须判断本单元格是否为百分数。

在cell.getCellType()中是得不到想要的结果的,不过poi提供了另一种方式:
cell.getCellStyle().getDataFormatString()

例如保留一位小数的百分数调用此方法将得到这样一个字符串:
0.0%
因此,剩下的我们只需要判断这个dataFormatString中是否包含百分号即可确认是百分数。剩下的就是把取得的double值乘以100并在结尾加上个百分号即可
相关标签: excel poi