org.ttzero.excel.reader.Sheet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eec Show documentation
Show all versions of eec Show documentation
A fast and lower memory excel write/read tool
一个非POI底层支持流式处理的高效且超低内存的Excel读写工具
The newest version!
/*
* Copyright (c) 2017-2019, [email protected] All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ttzero.excel.reader;
import org.ttzero.excel.manager.Const;
import org.ttzero.excel.util.CSVUtil;
import org.ttzero.excel.util.FileUtil;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalTime;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static org.ttzero.excel.reader.Cell.BOOL;
import static org.ttzero.excel.reader.Cell.DECIMAL;
import static org.ttzero.excel.reader.Cell.DOUBLE;
import static org.ttzero.excel.reader.Cell.FUNCTION;
import static org.ttzero.excel.reader.Cell.INLINESTR;
import static org.ttzero.excel.reader.Cell.LONG;
import static org.ttzero.excel.reader.Cell.NUMERIC;
import static org.ttzero.excel.reader.Cell.SST;
import static org.ttzero.excel.util.DateUtil.toTimeChars;
import static org.ttzero.excel.util.DateUtil.toDateTimeString;
import static org.ttzero.excel.util.DateUtil.toLocalDate;
import static org.ttzero.excel.util.DateUtil.toLocalTime;
import static org.ttzero.excel.util.DateUtil.toTimestamp;
import static org.ttzero.excel.util.FileUtil.exists;
/**
* 用于读的工作表,为了性能本工具将读和写分开设计它们具有完全不同的方法,
* 读取数据时可以通过{@link #header}方法指定表头位置,多行表头时可以指定一个起始行和结束行
* 来匹配,它将以{@code 行1:行2...行n}拼按的形式做为Key
*
* @author guanquan.wang at 2019-04-17 11:36
*/
public interface Sheet extends Closeable {
/**
* 获取工作表的名称
*
* @return 工作表名
*/
String getName();
/**
* 获取工作表在工作薄中的下标(从0开始)
*
* @return 工作表下标(从0开始)
*/
int getIndex();
/**
* 工作表id,它与索引的区别在于,id不会因为移动或删除工作表而更改。
*
* @return 工作表id
*/
int getId();
/**
* 获取当前工作表的总行数
*
* @return 当前工作表的总行数,{@code -1}表示未知
*/
default int getSize() {
Dimension d = getDimension();
return d != null ? d.lastRow - d.firstRow + 1 : -1;
}
/**
* 获取当前工作表中有效区域的范围地址(任意值,样式均表示有效值),此值取于头信息<dimension>的值,
* 如果头信息没有此值则读取最后一行的范围,此值并不能完全反映工作表的有效行数
*
* @return 当前工作表的有效区域
*/
Dimension getDimension();
/**
* 判断当前工作表是否隐藏
*
* @return {@code true} 当前为“隐藏”工作表
*/
boolean isHidden();
/**
* 判断当前工作表是否显示
*
* @return {@code true} 当前为“显示”工作表
*/
default boolean isShow() {
return !isHidden();
}
/**
* 设置工作表的表头行号(从1开始)与Excel看到的行号一致
*
* @param fromRowNum 表头行的位置(从1开始)
* @return 当前工作表
* @throws IndexOutOfBoundsException 如果{@code fromRow}小于1
*/
default Sheet header(int fromRowNum) {
return header(fromRowNum, fromRowNum);
}
/**
* 设置工作表的表头行号
*
* 注意: 指定标题行号后,行指针将移动到标题范围的下一行. 以下方法 {@link #bind(Class)}, {@link #bind(Class, int)},
* {@link #bind(Class, int, int)}, {@link #rows()}, {@link #dataRows()}, {@link #iterator()},
* 和 {@link #dataIterator()} 将受影响.
*
* @param fromRowNum 表头行的开始位置(从1开始,包含)
* @param toRowNum 表头行的结束位置(从1开始,包含)
* @return 当前工作表
* @throws IndexOutOfBoundsException 如果{@code fromRow}小于1
* @throws IllegalArgumentException 如果{@code toRow}小于{@code fromRow}
*/
Sheet header(int fromRowNum, int toRowNum);
/**
* 获取当前工作表表头,返回{@link #header}方法设置表头,未指定表头位置时默认取第一个非空行做为表头,如果为多行表头则将使用{@code ':'}拼接
*
*
* +-----------------------------+
* | | 收件人 |
* | 订单号 +-------+------+------+
* | | 省 | 市 | 区 |
* +-------+-------+------+------+
*
*
* 以上表头将返回 "订单号", "收件人:省", "收件人:市", "收件人:区"
*
* @return 表头行
*/
Row getHeader();
/**
* 绑定数据类型,后续可以通过{@link Row#get}方法直接将行数据转为指定的对象
*
* @param clazz 行数据需要转换的对象类型
* @return 当前工作表
*/
Sheet bind(Class> clazz);
/**
* 绑定数据类型并指定表头行号,后续可以通过{@link Row#get}方法直接将行数据转为指定的对象
*
* @param clazz 行数据需要转换的对象类型
* @param fromRowNum 表头行的位置(从1开始)
* @return 当前工作表
*/
default Sheet bind(Class> clazz, int fromRowNum) {
return bind(clazz, header(fromRowNum).getHeader());
}
/**
* 绑定数据类型并指定表头行号,后续可以通过{@link Row#get}方法直接将行数据转为指定的对象
*
* @param clazz 行数据需要转换的对象类型
* @param fromRowNum 表头行的位置(从1开始)
* @param toRowNum 表头行的结束位置(从1开始,包含)
* @return 当前工作表
*/
default Sheet bind(Class> clazz, int fromRowNum, int toRowNum) {
return bind(clazz, header(fromRowNum, toRowNum).getHeader());
}
/**
* 绑定数据类型并指定表头,后续可以通过{@link Row#get}方法直接将行数据转为指定的对象
*
* @param clazz 行数据需要转换的对象类型
* @param row 自定义表头
* @return 当前工作表
*/
Sheet bind(Class> clazz, Row row);
/**
* 加载工作表,读取工作表之前必须先使用此方法加载,使用Reader的场景已默认加载无需手动加载
*
* @return 当前工作表
* @throws IOException 读取异常
*/
Sheet load() throws IOException;
/**
* 构建一个行迭代器(包含空行),注意返回的{@code Row}对象是内存共享的所以不能直接收集,
* 收集数据前需要使用{@link Row#to}方法转为对象或者使用{@link Row#toMap}方法转为Map再收集。
*
* @return 行迭代器
*/
Iterator iterator();
/**
* 构建一个行迭代器(不包含空行),注意返回的{@code Row}对象是内存共享的所以不能直接收集,
* 收集数据前需要使用{@link Row#to}方法转为对象或者使用{@link Row#toMap}方法转为Map再收集。
*
* @return 行迭代器
*/
Iterator dataIterator();
/**
* 获取当前工作表包含的所有图片
*
* @return 图片列表,如果不包含图片则返回{@code null}
*/
List listPictures();
/**
* 重置游标以重头开始读,可以起到重复读的用处,不过此方法不是必要的,也可以直接通过reader获取对应工作表也可以
*
* @return 当前工作表
* @throws ExcelReadException 读取异常
* @throws UnsupportedOperationException 如果实现类不支持重复读时抛此异常
*/
default Sheet reset() {
throw new UnsupportedOperationException();
}
/**
* 返回一个行流,它与{@link #iterator()}具有相同的功能
*
* @return 行流
*/
default Stream rows() {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
iterator(), Spliterator.ORDERED | Spliterator.NONNULL), false);
}
/**
* 返回一个非空行流,它与{@link #dataIterator}具有相同的功能
*
* @return 非空行流
*/
default Stream dataRows() {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
dataIterator(), Spliterator.ORDERED | Spliterator.NONNULL), false);
}
/**
* 将当前工作表另存为{@code CSV}格式并保存到{@code path}文件中,默认以{@code UTF-8}字符集保存
*
* @param path 另存为CSV文件路径
* @throws IOException 读写异常
*/
default void saveAsCSV(Path path) throws IOException {
saveAsCSV(path, StandardCharsets.UTF_8);
}
/**
* 将当前工作表另存为{@code CSV}格式以{@code UTF-8}字符集保存保存到{@code path}文件中
*
* @param path 另存为CSV文件路径
* @param charset 指定字符集
* @throws IOException 读写异常
*/
default void saveAsCSV(Path path, Charset charset) throws IOException {
// Create path if not exists
if (!exists(path)) {
FileUtil.mkdir(path);
}
if (Files.isDirectory(path)) {
path = path.resolve(getName() + Const.Suffix.CSV);
}
saveAsCSV(Files.newOutputStream(path), charset);
}
/**
* 将当前工作表另存为{@code CSV}格式并输出到指定字节流
*
* @param os 输出字节流
* @throws IOException 读写异常
*/
default void saveAsCSV(OutputStream os) throws IOException {
saveAsCSV(os, StandardCharsets.UTF_8);
}
/**
* 将当前工作表另存为{@code CSV}格式并指定字符集然后输出到指定流
*
* @param os 输出流
* @param charset 字符集
* @throws IOException 读写异常
*/
default void saveAsCSV(OutputStream os, Charset charset) throws IOException {
saveAsCSV(new BufferedWriter(new OutputStreamWriter(os, charset)));
}
/**
* 将当前工作表另存为{@code CSV}格式并输出到指定流
*
* @param bw 输出流
* @throws IOException 读写异常
*/
default void saveAsCSV(BufferedWriter bw) throws IOException {
try (CSVUtil.Writer writer = CSVUtil.newWriter(bw)) {
int rowNum = 1;
for (Iterator iter = iterator(); iter.hasNext(); ) {
Row row = iter.next();
// 保持与xlsx相同行号
if (row.getRowNum() - rowNum > 1) {
for (; ++rowNum < row.getRowNum(); writer.newLine());
} else rowNum = row.getRowNum();
if (row.isBlank()) {
writer.newLine();
continue;
}
for (int i = 0; i < row.lc; i++) {
Cell c = row.cells[i];
switch (c.t) {
case SST:
if (c.stringVal == null) c.setString(row.sst.get(c.intVal));
case INLINESTR:
case FUNCTION:
writer.write(c.stringVal);
break;
case NUMERIC:
if (!row.styles.isDate(c.xf)) writer.write(c.intVal);
else if (c.intVal > 0) writer.write(toLocalDate(c.intVal).toString());
// 时分秒为00:00:00时读取为整数0
else writer.write(toTimeChars(LocalTime.MIN));
break;
case LONG:
writer.write(c.longVal);
break;
case DECIMAL:
if (!row.styles.isDate(c.xf)) writer.write(c.decimal.toString());
else if (c.decimal.compareTo(BigDecimal.ONE) >= 0) writer.write(toDateTimeString(toTimestamp(c.decimal.doubleValue())));
else writer.write(toTimeChars(toLocalTime(c.decimal.doubleValue())));
break;
case DOUBLE:
if (!row.styles.isDate(c.xf)) writer.write(c.doubleVal);
else if (c.doubleVal >= 1.0D) writer.write(toDateTimeString(toTimestamp(c.doubleVal)));
else writer.write(toTimeChars(toLocalTime(c.doubleVal)));
break;
case BOOL:
writer.write(c.boolVal);
break;
default:
writer.writeEmpty();
}
}
writer.newLine();
}
}
}
/**
* 强制匹配,即使没有{@link org.ttzero.excel.annotation.ExcelColumn}注解的字段也会强制匹配
*
* 将行数据转换为Java对象时默认情况下只匹配带有ExcelColumn注释的字段。
* {@code forceImport}将跳过此限制,有(ExcelColumn注释的依然按注解匹配,
* 没有(ExcelColumn注释的字段将与字段名匹配
*
* @return 当前工作表
*/
default Sheet forceImport() {
return addHeaderColumnReadOption(HeaderRow.FORCE_IMPORT);
}
/**
* 设置忽略大小写匹配表头字段
*
* @return 当前工作表
*/
default Sheet headerColumnIgnoreCase() {
return addHeaderColumnReadOption(HeaderRow.IGNORE_CASE);
}
/**
* 设置驼峰风格匹配表头字段
*
* @return 当前工作表
*/
default Sheet headerColumnToCamelCase() {
return addHeaderColumnReadOption(HeaderRow.CAMEL_CASE);
}
/**
* 添加表头读取属性
*
* @param option 额外属性
* @return 当前工作表
*/
default Sheet addHeaderColumnReadOption(int option) {
return setHeaderColumnReadOption(getHeaderColumnReadOption() | option);
}
/**
* 设置表头读取属性,将行数据转对象时由于Excel中的值与Java对象中定义的不同会使双方不匹配,设置读取属性可丰富读取能力,
* 多个属性可叠加
*
*
* - HeaderRow.FORCE_IMPORT: 强制导入,即使没有(ExcelColumn注解
* - HeaderRow.IGNORE_CASE: 忽略大小写匹配
* - HeaderRow.CAMEL_CASE: 驼峰风格匹配
*
* reader.sheet(0).setHeaderColumnReadOption(HeaderRow.FORCE_IMPORT | HeaderRow.IGNORE_CASE)
*
* @param option 额外属性
* @return 当前工作表
*/
Sheet setHeaderColumnReadOption(int option);
/**
* 获取表头读取属性
*
* @return 属性值
*/
int getHeaderColumnReadOption();
/**
* 将工作表转为普通工作表{@code Sheet},它只专注获取值
*
* @return {@link Sheet}
*/
Sheet asSheet();
/**
* 将工作表转为{@code CalcSheet}以解析单元格公式
*
* @return {@link CalcSheet}
*/
CalcSheet asCalcSheet();
/**
* 将工作表转为{@code MergeSheet},它将复制合并单元格的首坐标值到合并范围内的其它单元格中
*
* @return {@link MergeSheet}
*/
MergeSheet asMergeSheet();
/**
* 将工作表转为{@code FullSheet}支持全属性读取
*
* @return {@link FullSheet}
*/
FullSheet asFullSheet();
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy