美文网首页
2019-11-17 Java使用POI读写Excel文件:从入

2019-11-17 Java使用POI读写Excel文件:从入

作者: NoelleMu | 来源:发表于2019-11-17 16:11 被阅读0次

    最近需要实现一个将几个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提示我运行了两个测试,一个是我刚刚写好的这个,另一个是自带的,暂时不用管它)

    相关文章

      网友评论

          本文标题:2019-11-17 Java使用POI读写Excel文件:从入

          本文链接:https://www.haomeiwen.com/subject/bsmpictx.html