zoukankan      html  css  js  c++  java
  • JAVA使用hutool poi工具读取Excel表格指定行列范围的数据

    1.pom.xml依赖配置

            <!-- huTool工具箱 -->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.7.19</version>
            </dependency>
    
            <!-- poi -->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>5.1.0</version>
            </dependency>

    2.代码

    单元格数据模型对象

    package com.hdwang.exceltest;
    
    import cn.hutool.json.JSONUtil;
    
    /**
     * 单元格数据
     */
    public class CellData {
    
        /**
         * 行号
         */
        private int rowIndex;
    
        /**
         * 列号
         */
        private int cellIndex;
    
        /**
         * 单元格数值
         */
        private Object value;
    
        public int getRowIndex() {
            return rowIndex;
        }
    
        public void setRowIndex(int rowIndex) {
            this.rowIndex = rowIndex;
        }
    
        public int getCellIndex() {
            return cellIndex;
        }
    
        public void setCellIndex(int cellIndex) {
            this.cellIndex = cellIndex;
        }
    
        public Object getValue() {
            return value;
        }
    
        public void setValue(Object value) {
            this.value = value;
        }
    
        @Override
        public String toString() {
            return JSONUtil.toJsonStr(this);
        }
    }

    Bean对象属性赋值注解

    package com.hdwang.exceltest;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 列号,用于给bean对象的属性赋指定列的值
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {ElementType.FIELD})
    public @interface ColIndex {
    
        /**
         * 列索引号(从0开始),与name二者填一个即可,优先级高于name
         *
         * @return
         */
        int index() default -1;
    
        /**
         * 列名称(从A开始),与index二者填一个即可,优先级低于index
         *
         * @return
         */
        String name() default "";
    }

    表格数据对象

    package com.hdwang.exceltest;
    
    import cn.hutool.json.JSONUtil;
    
    /**
     * 证券月报
     */
    public class ZhenquanReport {
    
        /**
         * 名称
         */
        @ColIndex(name = "B")
        private String name;
    
        /**
         * 数值
         */
        @ColIndex(index = 2)
        private String value;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getValue() {
            return value;
        }
    
        public void setValue(String value) {
            this.value = value;
        }
    
        @Override
        public String toString() {
            return JSONUtil.toJsonStr(this);
        }
    }

    读取方法代码

    package com.hdwang.exceltest;
    
    import cn.hutool.poi.excel.ExcelReader;
    import cn.hutool.poi.excel.ExcelUtil;
    import cn.hutool.poi.excel.ExcelWriter;
    import cn.hutool.poi.excel.cell.CellHandler;
    import org.apache.commons.collections4.CollectionUtils;
    import org.apache.poi.ss.usermodel.*;
    
    import java.io.File;
    import java.lang.reflect.Field;
    import java.util.*;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class Main {
        public static void main(String[] args) {
            try {
                File templateFile = new File("C:\\Users\\hdwang\\Desktop\\test.xlsx");
                List<List<CellData>> rowDataList = readExcelData(templateFile, 2, Integer.MAX_VALUE, 1, Integer.MAX_VALUE);
                System.out.println(rowDataList);
    
                List<ZhenquanReport> reports = convertExcelDataToBeanList(rowDataList, ZhenquanReport.class);
                System.out.println(reports);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    /**
         * 读取表格数据
         *
         * @param templateFile   文件
         * @param startRowIndex  起始行号(从0开始)
         * @param endRowIndex    结束行号(从0开始)
         * @param startCellIndex 起始列号(从0开始)
         * @param endCellIndex   结束列号(从0开始)
         * @return 表格数据
         */
        private static List<List<CellData>> readExcelData(File templateFile, int startRowIndex, int endRowIndex, int startCellIndex, int endCellIndex) {
            ExcelReader excelReader = ExcelUtil.getReader(templateFile, 0);
            List<List<CellData>> rowDataList = new ArrayList<>();
            AtomicInteger rowIndex = new AtomicInteger(-1);
            excelReader.read(startRowIndex, endRowIndex, new CellHandler() {
                @Override
                public void handle(Cell cell, Object value) {
                    if (cell == null) {
                        //无单元格跳过
                        return;
                    }
                    if (cell.getColumnIndex() < startCellIndex || cell.getColumnIndex() > endCellIndex) {
                        //列号不在范围内跳过
                        return;
                    }
    
                    //新行的数据
                    if (cell.getRowIndex() != rowIndex.get()) {
                        rowDataList.add(new ArrayList<>());
                    }
                    rowIndex.set(cell.getRowIndex());
                    //取出新行数据对象存储单元格数据
                    List<CellData> cellDataList = rowDataList.get(rowDataList.size() - 1);
                    CellData cellData = new CellData();
                    cellData.setRowIndex(cell.getRowIndex());
                    cellData.setCellIndex(cell.getColumnIndex());
                    cellData.setValue(value);
                    cellDataList.add(cellData);
                }
            });
            return rowDataList;
        }
    
        /**
         * 转换表格数据为bean对象列表
         *
         * @param rowDataList 表格数据
         * @param tClass      bean类型
         * @param <T>
         * @return bean对象列表
         */
        private static <T> List<T> convertExcelDataToBeanList(List<List<CellData>> rowDataList, Class<T> tClass) {
            if (CollectionUtils.isEmpty(rowDataList)) {
                return new ArrayList<>();
            }
            List<T> beanList = new ArrayList<>();
            for (List<CellData> rowData : rowDataList) {
                try {
                    //实例化bean对象
                    T bean = tClass.newInstance();
                    //遍历字段并赋值
                    Field[] fields = tClass.getDeclaredFields();
                    for (Field field : fields) {
                        if (field.isAnnotationPresent(ColIndex.class)) {
                            ColIndex colIndex = field.getAnnotation(ColIndex.class);
                            int index = colIndex.index();
                            String name = colIndex.name();
                            if (index != -1) {
                                //do nothing
                            } else if (!"".equals(name)) {
                                //列名转索引号(补0为了适应下述方法)
                                index = ExcelUtil.colNameToIndex(name + "0");
                            } else {
                                throw new RuntimeException("请设置列号(ColIndex注解值必须配置一个)");
                            }
                            //从行数据中找到指定单元格数据给字段赋值
                            final int i = index;
                            CellData cellData = rowData.stream().filter(x -> x.getCellIndex() == i).findFirst().orElse(null);
                            if (cellData != null) {
                                Object value = cellData.getValue();
                                if (field.getType().getName().equals(String.class.getName())) {
                                    value = String.valueOf(value);
                                }
                                field.setAccessible(true);
                                field.set(bean, value);
                            }
                        }
                    }
                    beanList.add(bean);
                } catch (Exception ex) {
                    throw new RuntimeException("实例化对象失败", ex);
                }
            }
            return beanList;
        }
    }

    3.表格

    4.输出结果

    表格数据对象

    [
        [
            {
                "cellIndex": 1,
                "rowIndex": 2,
                "value": "净资产"
            },
            {
                "cellIndex": 2,
                "rowIndex": 2,
                "value": 10000
            },
            {
                "cellIndex": 3,
                "rowIndex": 2,
                "value": " "
            },
            {
                "cellIndex": 4,
                "rowIndex": 2,
                "value": 1
            }
        ],
        [
            {
                "cellIndex": 1,
                "rowIndex": 3,
                "value": "市值"
            },
            {
                "cellIndex": 2,
                "rowIndex": 3,
                "value": 20000
            },
            {
                "cellIndex": 4,
                "rowIndex": 3,
                "value": 2
            }
        ],
        [
            {
                "cellIndex": 1,
                "rowIndex": 4,
                "value": "标题"
            }
        ],
        [
            {
                "cellIndex": 1,
                "rowIndex": 5,
                "value": "净利润"
            },
            {
                "cellIndex": 2,
                "rowIndex": 5,
                "value": 1000
            },
            {
                "cellIndex": 4,
                "rowIndex": 5,
                "value": 3
            }
        ]
    ]

     转成bean对象列表

    [{"name":"净资产","value":"10000"}, {"name":"市值","value":"20000"}, {"name":"标题"}, {"name":"净利润","value":"1000"}]

    5.原理总结

    hutool poi 工具对POI进行了包装,实现了很多功能函数。可是在项目实践中发现还是有所不足。现在自己编写自定义取行列范围的数据并实现模型转换功能,从而方便对表格实现数据读取与单元格定位操作等。相关实现技术原理如下:

    (1) 基于 public void read(int startRowIndex, int endRowIndex, CellHandler cellHandler)  函数实现行过滤,在CellHandler内部实现列过滤,这样便实现了行列过滤,且取出了行列信息,便于对单元格定位等。

    (2) 将CellData转换为Bean对象的时候,采用反射技术读取bean对象字段的注解配置的列号信息,从而找到指定的CellData取值并赋值到字段上去。

    6.附录

    最新源码文件:https://github.com/hdwang123/exceltest

  • 相关阅读:
    AcWing 1135. 新年好 图论 枚举
    uva 10196 将军 模拟
    LeetCode 120. 三角形最小路径和 dp
    LeetCode 350. 两个数组的交集 II 哈希
    LeetCode 174. 地下城游戏 dp
    LeetCode 面试题 16.11.. 跳水板 模拟
    LeetCode 112. 路径总和 递归 树的遍历
    AcWing 1129. 热浪 spfa
    Thymeleaf Javascript 取值
    Thymeleaf Javascript 取值
  • 原文地址:https://www.cnblogs.com/hdwang/p/15774601.html
Copyright © 2011-2022 走看看