From 3bca95c34ab057c9211b1e03f27a15d48747afe4 Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 17 Jun 2020 00:53:34 +0800 Subject: [PATCH] add read --- CHANGELOG.md | 1 + .../java/cn/hutool/poi/excel/ExcelReader.java | 174 +++++++++++------- .../cn/hutool/poi/excel/cell/CellEditor.java | 7 +- .../cn/hutool/poi/excel/cell/CellHandler.java | 20 ++ .../hutool/poi/excel/test/ExcelReadTest.java | 6 + 5 files changed, 144 insertions(+), 64 deletions(-) create mode 100644 hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellHandler.java diff --git a/CHANGELOG.md b/CHANGELOG.md index da3a74429..992c4315c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * 【core 】 增加StrUtil.removeAny方法(issue#923@Github) * 【db 】 增加部分Connection参数支持(issue#924@Github) * 【core 】 FileUtil增加别名方法(pr#926@Github) +* 【poi 】 EcelReader中增加read重载,提供每个单元格单独处理的方法(issue#I1JZTL@Gitee) ### Bug修复 * 【json 】 修复append方法导致的JSONConfig传递失效问题(issue#906@Github) diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelReader.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelReader.java index 5c8cbb013..47b9fa924 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelReader.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelReader.java @@ -9,9 +9,11 @@ import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.poi.excel.cell.CellEditor; +import cn.hutool.poi.excel.cell.CellHandler; import cn.hutool.poi.excel.cell.CellUtil; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.extractor.ExcelExtractor; +import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; @@ -28,25 +30,32 @@ import java.util.Map; /** * Excel读取器
* 读取Excel工作簿 - * + * * @author Looly * @since 3.1.0 */ public class ExcelReader extends ExcelBase { - /** 是否忽略空行 */ + /** + * 是否忽略空行 + */ private boolean ignoreEmptyRow = true; - /** 单元格值处理接口 */ + /** + * 单元格值处理接口 + */ private CellEditor cellEditor; - /** 标题别名 */ + /** + * 标题别名 + */ private Map headerAlias = new HashMap<>(); // ------------------------------------------------------------------------------------------------------- Constructor start + /** * 构造 - * + * * @param excelFilePath Excel文件路径,绝对路径或相对于ClassPath路径 - * @param sheetIndex sheet序号,0表示第一个sheet + * @param sheetIndex sheet序号,0表示第一个sheet */ public ExcelReader(String excelFilePath, int sheetIndex) { this(FileUtil.file(excelFilePath), sheetIndex); @@ -54,8 +63,8 @@ public class ExcelReader extends ExcelBase { /** * 构造 - * - * @param bookFile Excel文件 + * + * @param bookFile Excel文件 * @param sheetIndex sheet序号,0表示第一个sheet */ public ExcelReader(File bookFile, int sheetIndex) { @@ -64,8 +73,8 @@ public class ExcelReader extends ExcelBase { /** * 构造 - * - * @param bookFile Excel文件 + * + * @param bookFile Excel文件 * @param sheetName sheet名,第一个默认是sheet1 */ public ExcelReader(File bookFile, String sheetName) { @@ -74,9 +83,9 @@ public class ExcelReader extends ExcelBase { /** * 构造 - * - * @param bookStream Excel文件的流 - * @param sheetIndex sheet序号,0表示第一个sheet + * + * @param bookStream Excel文件的流 + * @param sheetIndex sheet序号,0表示第一个sheet * @param closeAfterRead 读取结束是否关闭流 */ public ExcelReader(InputStream bookStream, int sheetIndex, boolean closeAfterRead) { @@ -85,9 +94,9 @@ public class ExcelReader extends ExcelBase { /** * 构造 - * - * @param bookStream Excel文件的流 - * @param sheetName sheet名,第一个默认是sheet1 + * + * @param bookStream Excel文件的流 + * @param sheetName sheet名,第一个默认是sheet1 * @param closeAfterRead 读取结束是否关闭流 */ public ExcelReader(InputStream bookStream, String sheetName, boolean closeAfterRead) { @@ -96,8 +105,8 @@ public class ExcelReader extends ExcelBase { /** * 构造 - * - * @param book {@link Workbook} 表示一个Excel文件 + * + * @param book {@link Workbook} 表示一个Excel文件 * @param sheetIndex sheet序号,0表示第一个sheet */ public ExcelReader(Workbook book, int sheetIndex) { @@ -106,8 +115,8 @@ public class ExcelReader extends ExcelBase { /** * 构造 - * - * @param book {@link Workbook} 表示一个Excel文件 + * + * @param book {@link Workbook} 表示一个Excel文件 * @param sheetName sheet名,第一个默认是sheet1 */ public ExcelReader(Workbook book, String sheetName) { @@ -116,7 +125,7 @@ public class ExcelReader extends ExcelBase { /** * 构造 - * + * * @param sheet Excel中的sheet */ public ExcelReader(Sheet sheet) { @@ -125,9 +134,10 @@ public class ExcelReader extends ExcelBase { // ------------------------------------------------------------------------------------------------------- Constructor end // ------------------------------------------------------------------------------------------------------- Getters and Setters start + /** * 是否忽略空行 - * + * * @return 是否忽略空行 */ public boolean isIgnoreEmptyRow() { @@ -136,7 +146,7 @@ public class ExcelReader extends ExcelBase { /** * 设置是否忽略空行 - * + * * @param ignoreEmptyRow 是否忽略空行 * @return this */ @@ -148,7 +158,7 @@ public class ExcelReader extends ExcelBase { /** * 设置单元格值处理逻辑
* 当Excel中的值并不能满足我们的读取要求时,通过传入一个编辑接口,可以对单元格值自定义,例如对数字和日期类型值转换为字符串等 - * + * * @param cellEditor 单元格值处理接口 * @return this */ @@ -159,7 +169,7 @@ public class ExcelReader extends ExcelBase { /** * 获得标题行的别名Map - * + * * @return 别名Map */ public Map getHeaderAlias() { @@ -168,7 +178,7 @@ public class ExcelReader extends ExcelBase { /** * 设置标题行的别名Map - * + * * @param headerAlias 别名Map * @return this */ @@ -179,9 +189,9 @@ public class ExcelReader extends ExcelBase { /** * 增加标题别名 - * + * * @param header 标题 - * @param alias 别名 + * @param alias 别名 * @return this */ public ExcelReader addHeaderAlias(String header, String alias) { @@ -191,7 +201,7 @@ public class ExcelReader extends ExcelBase { /** * 去除标题别名 - * + * * @param header 标题 * @return this */ @@ -203,7 +213,7 @@ public class ExcelReader extends ExcelBase { /** * 读取工作簿中指定的Sheet的所有行列数据 - * + * * @return 行的集合,一行使用List表示 */ public List> read() { @@ -212,7 +222,7 @@ public class ExcelReader extends ExcelBase { /** * 读取工作簿中指定的Sheet - * + * * @param startRowIndex 起始行(包含,从0开始计数) * @return 行的集合,一行使用List表示 * @since 4.0.0 @@ -223,12 +233,12 @@ public class ExcelReader extends ExcelBase { /** * 读取工作簿中指定的Sheet - * + * * @param startRowIndex 起始行(包含,从0开始计数) - * @param endRowIndex 结束行(包含,从0开始计数) + * @param endRowIndex 结束行(包含,从0开始计数) * @return 行的集合,一行使用List表示 */ - @SuppressWarnings({ "rawtypes", "unchecked" }) + @SuppressWarnings({"rawtypes", "unchecked"}) public List> read(int startRowIndex, int endRowIndex) { checkNotClosed(); List> resultList = new ArrayList<>(); @@ -255,10 +265,49 @@ public class ExcelReader extends ExcelBase { return resultList; } + /** + * 读取工作簿中指定的Sheet,此方法为类流处理方式,当读到指定单元格时,会调用CellEditor接口
+ * 用户通过实现此接口,可以更加灵活的处理每个单元格的数据。 + * + * @param cellHandler 单元格处理器,用于处理读到的单元格及其数据 + * @since 5.3.8 + */ + public void read(CellHandler cellHandler) { + read(0, Integer.MAX_VALUE, cellHandler); + } + + /** + * 读取工作簿中指定的Sheet,此方法为类流处理方式,当读到指定单元格时,会调用CellEditor接口
+ * 用户通过实现此接口,可以更加灵活的处理每个单元格的数据。 + * + * @param startRowIndex 起始行(包含,从0开始计数) + * @param endRowIndex 结束行(包含,从0开始计数) + * @param cellHandler 单元格处理器,用于处理读到的单元格及其数据 + * @since 5.3.8 + */ + public void read(int startRowIndex, int endRowIndex, CellHandler cellHandler) { + checkNotClosed(); + + startRowIndex = Math.max(startRowIndex, this.sheet.getFirstRowNum());// 读取起始行(包含) + endRowIndex = Math.min(endRowIndex, this.sheet.getLastRowNum());// 读取结束行(包含) + + Row row; + short columnSize; + for (int y = startRowIndex; y <= endRowIndex; y++) { + row = this.sheet.getRow(y); + columnSize = row.getLastCellNum(); + Cell cell; + for (short x = 0; x < columnSize; x++) { + cell = row.getCell(x); + cellHandler.handle(cell, CellUtil.getCellValue(cell)); + } + } + } + /** * 读取Excel为Map的列表,读取所有行,默认第一行做为标题,数据从第二行开始
* Map表示一行,标题为key,单元格内容为value - * + * * @return Map的列表 */ public List> readAll() { @@ -268,10 +317,10 @@ public class ExcelReader extends ExcelBase { /** * 读取Excel为Map的列表
* Map表示一行,标题为key,单元格内容为value - * + * * @param headerRowIndex 标题所在行,如果标题行在读取的内容行中间,这行做为数据将忽略 - * @param startRowIndex 起始行(包含,从0开始计数) - * @param endRowIndex 读取结束行(包含,从0开始计数) + * @param startRowIndex 起始行(包含,从0开始计数) + * @param endRowIndex 读取结束行(包含,从0开始计数) * @return Map的列表 */ public List> read(int headerRowIndex, int startRowIndex, int endRowIndex) { @@ -306,8 +355,8 @@ public class ExcelReader extends ExcelBase { /** * 读取Excel为Bean的列表,读取所有行,默认第一行做为标题,数据从第二行开始 - * - * @param Bean类型 + * + * @param Bean类型 * @param beanType 每行对应Bean的类型 * @return Map的列表 */ @@ -317,11 +366,11 @@ public class ExcelReader extends ExcelBase { /** * 读取Excel为Bean的列表 - * - * @param Bean类型 + * + * @param Bean类型 * @param headerRowIndex 标题所在行,如果标题行在读取的内容行中间,这行做为数据将忽略,,从0开始计数 - * @param startRowIndex 起始行(包含,从0开始计数) - * @param beanType 每行对应Bean的类型 + * @param startRowIndex 起始行(包含,从0开始计数) + * @param beanType 每行对应Bean的类型 * @return Map的列表 * @since 4.0.1 */ @@ -331,12 +380,12 @@ public class ExcelReader extends ExcelBase { /** * 读取Excel为Bean的列表 - * - * @param Bean类型 + * + * @param Bean类型 * @param headerRowIndex 标题所在行,如果标题行在读取的内容行中间,这行做为数据将忽略,,从0开始计数 - * @param startRowIndex 起始行(包含,从0开始计数) - * @param endRowIndex 读取结束行(包含,从0开始计数) - * @param beanType 每行对应Bean的类型 + * @param startRowIndex 起始行(包含,从0开始计数) + * @param endRowIndex 读取结束行(包含,从0开始计数) + * @param beanType 每行对应Bean的类型 * @return Map的列表 */ @SuppressWarnings("unchecked") @@ -357,7 +406,7 @@ public class ExcelReader extends ExcelBase { /** * 读取为文本格式
* 使用{@link ExcelExtractor} 提取Excel内容 - * + * * @param withSheetName 是否附带sheet名 * @return Excel文本 * @since 4.1.0 @@ -370,7 +419,7 @@ public class ExcelReader extends ExcelBase { /** * 获取 {@link ExcelExtractor} 对象 - * + * * @return {@link ExcelExtractor} * @since 4.1.0 */ @@ -387,7 +436,7 @@ public class ExcelReader extends ExcelBase { /** * 读取某一行数据 - * + * * @param rowIndex 行号,从0开始 * @return 一行数据 * @since 4.0.3 @@ -398,7 +447,7 @@ public class ExcelReader extends ExcelBase { /** * 读取某个单元格的值 - * + * * @param x X坐标,从0计数,即列号 * @param y Y坐标,从0计数,即行号 * @return 值,如果单元格无值返回null @@ -411,7 +460,7 @@ public class ExcelReader extends ExcelBase { /** * 获取Excel写出器
* 在读取Excel并做一定编辑后,获取写出器写出 - * + * * @return {@link ExcelWriter} * @since 4.0.6 */ @@ -420,9 +469,10 @@ public class ExcelReader extends ExcelBase { } // ------------------------------------------------------------------------------------------------------- Private methods start + /** * 读取一行 - * + * * @param row 行 * @return 单元格值列表 */ @@ -432,7 +482,7 @@ public class ExcelReader extends ExcelBase { /** * 转换标题别名,如果没有别名则使用原标题,当标题为空时,列号对应的字母便是header - * + * * @param headerList 原标题列表 * @return 转换别名列表 */ @@ -443,25 +493,25 @@ public class ExcelReader extends ExcelBase { return result; } - for(int i = 0; i < size; i++) { + for (int i = 0; i < size; i++) { result.add(aliasHeader(headerList.get(i), i)); } return result; } - + /** * 转换标题别名,如果没有别名则使用原标题,当标题为空时,列号对应的字母便是header - * + * * @param headerObj 原标题 - * @param index 标题所在列号,当标题为空时,列号对应的字母便是header + * @param index 标题所在列号,当标题为空时,列号对应的字母便是header * @return 转换别名列表 * @since 4.3.2 */ private String aliasHeader(Object headerObj, int index) { - if(null == headerObj) { + if (null == headerObj) { return ExcelUtil.indexToColName(index); } - + final String header = headerObj.toString(); return ObjectUtil.defaultIfNull(this.headerAlias.get(header), header); } diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellEditor.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellEditor.java index 8504989db..a6cbfd46c 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellEditor.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellEditor.java @@ -4,13 +4,16 @@ import org.apache.poi.ss.usermodel.Cell; /** * 单元格编辑器接口 - * @author Looly * + * @author Looly */ +@FunctionalInterface public interface CellEditor { + /** * 编辑 - * @param cell 单元格对象,可以获取单元格行、列样式等信息 + * + * @param cell 单元格对象,可以获取单元格行、列样式等信息 * @param value 单元格值 * @return 编辑后的对象 */ diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellHandler.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellHandler.java new file mode 100644 index 000000000..3fd404a9a --- /dev/null +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellHandler.java @@ -0,0 +1,20 @@ +package cn.hutool.poi.excel.cell; + +import org.apache.poi.ss.usermodel.Cell; + +/** + * 单元格处理器接口 + * + * @author Looly + */ +@FunctionalInterface +public interface CellHandler { + + /** + * 处理 + * + * @param cell 单元格对象,可以获取单元格行、列样式等信息 + * @param value 单元格值 + */ + void handle(Cell cell, Object value); +} diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/test/ExcelReadTest.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/test/ExcelReadTest.java index 9fe934b08..85742679c 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/test/ExcelReadTest.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/test/ExcelReadTest.java @@ -208,4 +208,10 @@ public class ExcelReadTest { Assert.assertEquals(11L, read.get(2).get(2)); } + @Test + public void readCellsTest() { + final ExcelReader reader = ExcelUtil.getReader("merge_test.xlsx"); + reader.read((cell, value)-> Console.log("{}, {} {}", cell.getRowIndex(), cell.getColumnIndex(), value)); + } + }