2019-11-17 Java使用POI读写Excel文件:从入门到放弃(持续更新中)

本文章持续更新中

最近需要实现一个将几个Excel表格合并成一个的功能。原本是想用脚本语言来实现的(因为那样方便一些),但是后来考虑到自己对Python等脚本语言并不是特别熟悉,还是决定使用最熟悉的Java来完成这个功能,顺便借这个机会学习一下POI的使用,复习一下Java语言基础并准备明年三月的蓝桥杯。

不要问我这个IDEA死忠粉为什么改用Eclipse了,要问就问蓝桥杯的组织者们为什么规定必须要用这么难用的东西吧

Maven依赖

POI的Maven依赖可以直接从Maven中央仓库中找到。我这里使用了最新的4.1.1版本。

<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.1</version>
</dependency>

用Eclipse建立Maven项目以及Eclipse自动补全功能的配置也是一个坑,但是网上的教程很多,这里就不详细记录了。

Excel操作工具类的实现

为了方便之后对Excel的操作,将一些常用的方法封装成工具类以便调用。

根据文件路径返回Workbook对象

实际上就是打开一个Workbook。

    /**
     * 根据文件路径返回Workbook对象
     * 
     * @param Excel文件的路径
     * 
     * @author 江文
     */
    private static HSSFWorkbook openWorkbook(String path) {
        // 定义IO流和workbook对象备用
        InputStream inputStream = null;
        HSSFWorkbook workbook = null;

        // 如果文件名为空
        if (path == null || path.equals("")) {
            throw new IllegalArgumentException("文件路径不能为空");
        }

        try {
            // 通过IO操作打开该Excel文档
            inputStream = new FileInputStream(path);
            workbook = new HSSFWorkbook(inputStream);
        } catch (FileNotFoundException e) {
            System.out.println("找不到该文件");
            e.printStackTrace();
        } catch (EncryptedDocumentException e) {
            System.out.println("该Excel文档有密码,请移除密码后再次尝试");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return workbook;
    }

打开某个Excel文件的一个Sheet

用到了上面那个打开Workbook的方法。

    /**
     * 根据文件名和sheet编号返回一个sheet
     * sheet编号如果为null则是打开全部的sheet,但是这个功能所涉及到的Excel文件都只有一个sheet,所以为null就直接抛异常
     * 
     * @param path    文件路径
     * @param sheetNo sheet的编号,从0开始
     * 
     * @author 江文
     */
    public static HSSFSheet getStringByPathAndSheet(String path, Integer sheetNo) throws Exception {
        // 打开Workbook
        HSSFWorkbook workbook = openWorkbook(path);

        // 打开失败(文件不存在或者openWorkbook方法出现异常)
        if (workbook == null) {
            throw new Exception("Excel文件打开失败");
        } else if (sheetNo == null) {
            throw new IllegalArgumentException("sheet编号不能为空");
        } else {
            // 文件打开成功,sheet编号也不为空,就直接返回这个sheet
            HSSFSheet sheet = workbook.getSheetAt(sheetNo);

            return sheet;
        }
    }

根据行和列返回单元格的内容

这里涉及到了单元格类型的问题。

POI定义单元格共有七种类型,如下(可以在CellType类中获取到):

   /**
     * Unknown type, used to represent a state prior to initialization or the
     * lack of a concrete type.
     * For internal use only.
     */
    @Internal(since="POI 3.15 beta 3")
    _NONE(-1),

    /**
     * Numeric cell type (whole numbers, fractional numbers, dates)
     */
    NUMERIC(0),
    
    /** String (text) cell type */
    STRING(1),
    
    /**
     * Formula cell type
     * @see FormulaType
     */
    FORMULA(2),
    
    /**
     * Blank cell type
     */
    BLANK(3),
    
    /**
     * Boolean cell type
     */
    BOOLEAN(4),
    
    /**
     * Error cell type
     * @see FormulaError
     */
    ERROR(5);

而最后我们需要的是String类型的返回值,所以要根据这五种情况分别作不同的处理:

    /**
     * 读取sheet中某一行某一列的单元格内容并返回
     * 
     * @param sheet   要读取的sheet
     * @param rownum  需要读取的单元格所在的行
     * @param cellnum 需要读取的单元格所在的列
     * 
     * @author 江文
     */
    public String getContentStringByRowAndCellNum(HSSFSheet sheet, Integer rownum, Integer cellnum) {
        // 根据数值获取row
        HSSFRow row = sheet.getRow(rownum);

        // 从row中获取cell
        HSSFCell cell = row.getCell(cellnum);

        // 根据单元格的不同,读取单元格的值,但是最后统一返回String类型
        // value变量存储返回值,默认为空,以便处理
        String value = "";

        // 根据单元格类型不同作不同处理
        switch (cell.getCellType()) {
        case _NONE:
            // 未知,返回字符串UNKNOWN
            value = "UNKNOWN";
            return value;
        case NUMERIC:
            // 数字型,转换成String再返回
            value = String.valueOf(cell.getNumericCellValue());
            return value;
        case STRING:
            // 字符串型,直接返回
            value = cell.getStringCellValue();
            return value;
        case FORMULA:
            // 公式型,直接返回
            value = cell.getCellFormula();
            return value;
        case BLANK:
            // 空,返回空字符串
            return value;
        case BOOLEAN:
            // 布尔型,返回true或者是false
            value = String.valueOf(cell.getBooleanCellValue());
            return value;
        case ERROR:
            // 错误类型,返回字符串ERROR
            value = "ERROR";
            return value;
        default:
            // 其他情况,也返回空字符串
            return value;
        } // switch
    } // getContentStringByRowAndCellNum
}

对getContentStringByRowAndCellNum()方法进行测试

因为这个功能比较复杂,为了保证最后不会一下出现很多问题,顺便也学学JUnit,于是对getContentStringByRowAndCellNum()方法写了一个单元测试。

实际上也顺便测试了另外两个方法。

自带的JUnit是3.8版本,太旧了,于是从Maven中央仓库中找到新版本的JUnit并修改pom.xml文件中JUnit相关的依赖,这里我用了4.12版本:

<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.12</version>
   <scope>test</scope>
</dependency>

在项目文件夹下新建一个Excel文件夹,放入一个test.xls,它的E2单元格中有一个数据:“4399”。

Maven的测试类命名模式如下:

  • **/Test*.java
  • **/*Test.java
  • **/*TestCase.java

按上述模式命名的类, 使用mvn test命令就能自动运行这些类。

建立ExcelUtilsTest.java文件:

package site.wendev.excelMerge;

import static org.junit.Assert.assertEquals;

import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.junit.Test;

import site.wendev.excelMerge.utils.ExcelUtils;

public class ExcelUtilsTest {
    // 预期结果
    String exceptedString = "4399";
    
    // 这个必须使用绝对路径否则会报错,使用~也会提示找不到文件
    String filePath = "/home/jiangwen/eclipse-workspace/excelMerge/Excel/test.xls";
    
    // 预期结果位于的行数
    Integer rownum = 1;
    
    // 预期结果位于的列数
    Integer cellnum = 4;
    
    
    /**
     * 测试getContentStringByRowAndCellNum方法
     * 实际上也是顺便测试了getSheetByPathAndSheet方法
     * (因为那两个不成功的话,getContentByRowAndCellNum也不会成功)
     * 
     * @author 江文
     */
    @Test
    public void testGetString() {
        HSSFSheet sheet = null;
        
        try {
            sheet = ExcelUtils.getSheetByPathAndSheet(filePath, 0);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        assertEquals(exceptedString, ExcelUtils.getContentStringByRowAndCellNum(sheet, rownum, cellnum));
    }
}

在项目文件夹下执行mvn test就可以进行测试了。

(Maven提示我运行了两个测试,一个是我刚刚写好的这个,另一个是自带的,暂时不用管它)

推荐阅读更多精彩内容

  • 使用首先需要了解他的工作原理 1.POI结构与常用类 (1)创建Workbook和Sheet (2)创建单元格 (...
    长城ol阅读 2,742评论 1 19
  • 转自链接 目录 1.认识NPOI 2.使用NPOI生成xls文件 2.1创建基本内容 2.1.1创建Workboo...
    腿毛裤阅读 2,644评论 0 1
  • POI操作Excel Excel简介一个excel文件就是一个工作簿workbook,一个工作簿中可以创建多张工作...
    野比大雄1024阅读 2,901评论 2 45
  • 1.导入poi的jar <dependency> <groupId>org.apache.poi</group...
    刘大锤阅读 253评论 0 0
  • 实习第二周 No.2 项目功能里要求能够将展示的报表导出excel,因为报表的数据都是动态从list传进来的,所...
    苏筱筑阅读 1,051评论 0 7