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
以上是 JAVA使用hutool poi工具读取Excel表格指定行列范围的数据 的全部内容, 来源链接: utcz.com/z/390348.html