This commit is contained in:
Looly 2020-11-10 10:33:29 +08:00
parent 37dda15906
commit 3081e25a4d
11 changed files with 172 additions and 42 deletions

View File

@ -3,7 +3,7 @@
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
# 5.5.0 (2020-11-08) # 5.5.0 (2020-11-09)
### 新特性 ### 新特性
* 【core 】 NumberUtil.parseInt等支持123,2.00这类数字issue#I23ORQ@Gitee * 【core 】 NumberUtil.parseInt等支持123,2.00这类数字issue#I23ORQ@Gitee
@ -20,6 +20,7 @@
* 【core 】 修复ClassUtil.getTypeArgument方法在判断泛型时导致的问题issue#1207@Github * 【core 】 修复ClassUtil.getTypeArgument方法在判断泛型时导致的问题issue#1207@Github
* 【core 】 修复Ipv4Util分隔符问题issue#I24A9I@Gitee * 【core 】 修复Ipv4Util分隔符问题issue#I24A9I@Gitee
* 【core 】 修复Ipv4Util.longToIp的问题 * 【core 】 修复Ipv4Util.longToIp的问题
* 【poi 】 修复Excel07SaxReader读取公式的错误的问题issue#I23VFL@Gitee
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------

View File

@ -33,7 +33,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable {
/** /**
* 创建字符串构建器 * 创建字符串构建器
* *
* @return {@link StrBuilder} * @return this
*/ */
public static StrBuilder create() { public static StrBuilder create() {
return new StrBuilder(); return new StrBuilder();
@ -43,7 +43,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable {
* 创建字符串构建器 * 创建字符串构建器
* *
* @param initialCapacity 初始容量 * @param initialCapacity 初始容量
* @return {@link StrBuilder} * @return this
*/ */
public static StrBuilder create(int initialCapacity) { public static StrBuilder create(int initialCapacity) {
return new StrBuilder(initialCapacity); return new StrBuilder(initialCapacity);
@ -53,7 +53,7 @@ public class StrBuilder implements CharSequence, Appendable, Serializable {
* 创建字符串构建器 * 创建字符串构建器
* *
* @param strs 初始字符串 * @param strs 初始字符串
* @return {@link StrBuilder} * @return this
* @since 4.0.1 * @since 4.0.1
*/ */
public static StrBuilder create(CharSequence... strs) { public static StrBuilder create(CharSequence... strs) {

View File

@ -2,17 +2,39 @@ package cn.hutool.poi.excel.cell;
/** /**
* 公式类型的值 * 公式类型的值
* *
* @author looly * @author looly
* @since 4.0.11 * @since 4.0.11
*/ */
public class FormulaCellValue implements CellValue<String> { public class FormulaCellValue implements CellValue<String> {
/** 公式 */ /**
* 公式
*/
String formula; String formula;
/**
* 结果使用ExcelWriter时可以不用
*/
Object result;
/**
* 构造
*
* @param formula 公式
*/
public FormulaCellValue(String formula) { public FormulaCellValue(String formula) {
this(formula, null);
}
/**
* 构造
*
* @param formula 公式
* @param result 结果
*/
public FormulaCellValue(String formula, Object result) {
this.formula = formula; this.formula = formula;
this.result = result;
} }
@Override @Override
@ -20,4 +42,16 @@ public class FormulaCellValue implements CellValue<String> {
return this.formula; return this.formula;
} }
/**
* 获取结果
* @return 结果
*/
public Object getResult() {
return this.result;
}
@Override
public String toString() {
return getResult().toString();
}
} }

View File

@ -11,15 +11,15 @@ public enum CellDataType {
BOOL("b"), BOOL("b"),
/** 类型错误 */ /** 类型错误 */
ERROR("e"), ERROR("e"),
/** 计算结果类型 */ /** 计算结果类型此类型使用f标签辅助判断而非属性 */
FORMULA("str"), FORMULA("formula"),
/** 富文本类型 */ /** 富文本类型 */
INLINESTR("inlineStr"), INLINESTR("inlineStr"),
/** 共享字符串索引类型 */ /** 共享字符串索引类型 */
SSTINDEX("s"), SSTINDEX("s"),
/** 数字类型 */ /** 数字类型 */
NUMBER(""), NUMBER(""),
/** 日期类型 */ /** 日期类型,此类型使用值判断,而非属性 */
DATE("m/d/yy"), DATE("m/d/yy"),
/** 空类型 */ /** 空类型 */
NULL(""); NULL("");

View File

@ -12,9 +12,17 @@ public enum ElementName {
*/ */
row, row,
/** /**
* 单元格标签名表示一个单元格 * Cell单元格标签名表示一个单元格
*/ */
c; c,
/**
* Value单元格值的标签表示单元格内的值
*/
v,
/**
* Formula公式表示一个存放公式的单元格
*/
f;
/** /**
* 给定标签名是否匹配当前标签 * 给定标签名是否匹配当前标签
@ -25,4 +33,17 @@ public enum ElementName {
public boolean match(String elementName){ public boolean match(String elementName){
return this.name().equals(elementName); return this.name().equals(elementName);
} }
/**
* 解析支持的节点名枚举
* @param elementName 节点名
* @return 节点名枚举
*/
public static ElementName of(String elementName){
try {
return valueOf(elementName);
} catch (Exception ignore){
}
return null;
}
} }

View File

@ -275,10 +275,10 @@ public class Excel03SaxReader implements HSSFListener, ExcelSaxReader<Excel03Sax
// This is stored in the next record // This is stored in the next record
isOutputNextStringRecord = true; isOutputNextStringRecord = true;
} else { } else {
value = formatListener.formatNumberDateCell(formulaRec); value = ExcelSaxUtil.getNumberOrDateValue(formulaRec, formulaRec.getValue(), this.formatListener);
} }
} else { } else {
value = StrUtil.wrap(HSSFFormulaParser.toFormulaString(stubWorkbook, formulaRec.getParsedExpression()), "\""); value = HSSFFormulaParser.toFormulaString(stubWorkbook, formulaRec.getParsedExpression());
} }
addToRowCellList(formulaRec, value); addToRowCellList(formulaRec, value);
break; break;
@ -305,19 +305,7 @@ public class Excel03SaxReader implements HSSFListener, ExcelSaxReader<Excel03Sax
break; break;
case NumberRecord.sid: // 数字类型 case NumberRecord.sid: // 数字类型
final NumberRecord numrec = (NumberRecord) record; final NumberRecord numrec = (NumberRecord) record;
if(ExcelSaxUtil.isDateFormat(numrec, formatListener)){ value = ExcelSaxUtil.getNumberOrDateValue(numrec, numrec.getValue(), this.formatListener);
// 可能为日期格式
value = ExcelSaxUtil.getDateValue(numrec.getValue());
} else {
final double doubleValue = numrec.getValue();
final long longPart = (long) doubleValue;
// 对于无小数部分的数字类型转为Long否则保留原数字
if (((double) longPart) == doubleValue) {
value = longPart;
} else {
value = doubleValue;
}
}
// 向容器加入列值 // 向容器加入列值
addToRowCellList(numrec, value); addToRowCellList(numrec, value);
break; break;

View File

@ -5,6 +5,7 @@ import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrBuilder; import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.cell.FormulaCellValue;
import cn.hutool.poi.excel.sax.handler.RowHandler; import cn.hutool.poi.excel.sax.handler.RowHandler;
import cn.hutool.poi.exceptions.POIException; import cn.hutool.poi.exceptions.POIException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
@ -54,6 +55,8 @@ public class Excel07SaxReader extends DefaultHandler implements ExcelSaxReader<E
private long rowNumber; private long rowNumber;
// 当前列坐标 如A1B5 // 当前列坐标 如A1B5
private String curCoordinate; private String curCoordinate;
// 当前节点名称
private ElementName curElementName;
// 前一个列的坐标 // 前一个列的坐标
private String preCoordinate; private String preCoordinate;
// 行的最大列坐标 // 行的最大列坐标
@ -65,6 +68,8 @@ public class Excel07SaxReader extends DefaultHandler implements ExcelSaxReader<E
// 上一次的内容 // 上一次的内容
private final StrBuilder lastContent = StrUtil.strBuilder(); private final StrBuilder lastContent = StrUtil.strBuilder();
// 上一次的内容
private final StrBuilder lastFormula = StrUtil.strBuilder();
// 存储每行的列元素 // 存储每行的列元素
private List<Object> rowCellList = new ArrayList<>(); private List<Object> rowCellList = new ArrayList<>();
@ -189,10 +194,20 @@ public class Excel07SaxReader extends DefaultHandler implements ExcelSaxReader<E
*/ */
@Override @Override
public void startElement(String uri, String localName, String qName, Attributes attributes) { public void startElement(String uri, String localName, String qName, Attributes attributes) {
if (ElementName.row.match(localName)) {// 行开始 final ElementName name = ElementName.of(localName);
startRow(attributes); this.curElementName = name;
} else if (ElementName.c.match(localName)) {// 单元格元素
startCell(attributes); if(null != name){
switch (name){
case row:
// 行开始
startRow(attributes);
break;
case c:
// 单元格元素
startCell(attributes);
break;
}
} }
} }
@ -201,6 +216,7 @@ public class Excel07SaxReader extends DefaultHandler implements ExcelSaxReader<E
*/ */
@Override @Override
public void endElement(String uri, String localName, String qName) { public void endElement(String uri, String localName, String qName) {
this.curElementName = null;
if (ElementName.c.match(localName)) { // 单元格结束 if (ElementName.c.match(localName)) { // 单元格结束
endCell(); endCell();
} else if (ElementName.row.match(localName)) {// 行结束 } else if (ElementName.row.match(localName)) {// 行结束
@ -213,8 +229,16 @@ public class Excel07SaxReader extends DefaultHandler implements ExcelSaxReader<E
*/ */
@Override @Override
public void characters(char[] ch, int start, int length) { public void characters(char[] ch, int start, int length) {
// 得到单元格内容的值 switch (this.curElementName){
lastContent.append(ch, start, length); case v:
// 得到单元格内容的值
lastContent.append(ch, start, length);
break;
case f:
// 得到单元格内容的值
lastFormula.append(ch, start, length);
break;
}
} }
// --------------------------------------------------------------------------------------- Private method start // --------------------------------------------------------------------------------------- Private method start
@ -299,16 +323,21 @@ public class Excel07SaxReader extends DefaultHandler implements ExcelSaxReader<E
// 清空之前的数据 // 清空之前的数据
lastContent.reset(); lastContent.reset();
lastFormula.reset();
} }
/** /**
* 一个单元格结尾 * 一个单元格结尾
*/ */
private void endCell() { private void endCell() {
final String contentStr = StrUtil.trim(lastContent);
final Object value = ExcelSaxUtil.getDataValue(this.cellDataType, contentStr, this.sharedStringsTable, this.numFmtString);
// 补全单元格之间的空格 // 补全单元格之间的空格
fillBlankCell(preCoordinate, curCoordinate, false); fillBlankCell(preCoordinate, curCoordinate, false);
final String contentStr = StrUtil.trim(lastContent);
Object value = ExcelSaxUtil.getDataValue(this.cellDataType, contentStr, this.sharedStringsTable, this.numFmtString);
if(false == this.lastFormula.isEmpty()){
value = new FormulaCellValue(StrUtil.trim(lastFormula), value);
}
addCellValue(curCell++, value); addCellValue(curCell++, value);
} }

View File

@ -8,7 +8,7 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.sax.handler.RowHandler; import cn.hutool.poi.excel.sax.handler.RowHandler;
import cn.hutool.poi.exceptions.POIException; import cn.hutool.poi.exceptions.POIException;
import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener; import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
import org.apache.poi.hssf.record.NumberRecord; import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.ooxml.util.SAXHelper; import org.apache.poi.ooxml.util.SAXHelper;
import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.xssf.model.SharedStringsTable; import org.apache.poi.xssf.model.SharedStringsTable;
@ -175,14 +175,14 @@ public class ExcelSaxUtil {
/** /**
* 判断数字Record中是否为日期格式 * 判断数字Record中是否为日期格式
* @param numrec 单元格记录 * @param cell 单元格记录
* @param formatListener {@link FormatTrackingHSSFListener} * @param formatListener {@link FormatTrackingHSSFListener}
* @return 是否为日期格式 * @return 是否为日期格式
* @since 5.4.8 * @since 5.4.8
*/ */
public static boolean isDateFormat(NumberRecord numrec, FormatTrackingHSSFListener formatListener){ public static boolean isDateFormat(CellValueRecordInterface cell, FormatTrackingHSSFListener formatListener){
final int formatIndex = formatListener.getFormatIndex(numrec); final int formatIndex = formatListener.getFormatIndex(cell);
final String formatString = formatListener.getFormatString(numrec); final String formatString = formatListener.getFormatString(cell);
return org.apache.poi.ss.usermodel.DateUtil.isADateFormat(formatIndex, formatString); return org.apache.poi.ss.usermodel.DateUtil.isADateFormat(formatIndex, formatString);
} }
@ -222,6 +222,31 @@ public class ExcelSaxUtil {
: new Excel03SaxReader(rowHandler); : new Excel03SaxReader(rowHandler);
} }
/**
* 在Excel03 sax读取中获取日期或数字类型的结果值
* @param cell 记录单元格
* @param value
* @param formatListener {@link FormatTrackingHSSFListener}
* @return 可能为Date或Double或Long
* @since 5.5.0
*/
public static Object getNumberOrDateValue(CellValueRecordInterface cell, double value, FormatTrackingHSSFListener formatListener){
Object result;
if(ExcelSaxUtil.isDateFormat(cell, formatListener)){
// 可能为日期格式
result = ExcelSaxUtil.getDateValue(value);
} else {
final long longPart = (long) value;
// 对于无小数部分的数字类型转为Long否则保留原数字
if (((double) longPart) == value) {
result = longPart;
} else {
result = value;
}
}
return result;
}
/** /**
* 获取数字类型值 * 获取数字类型值
* *

View File

@ -2,9 +2,11 @@ package cn.hutool.poi.excel.test;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Console; import cn.hutool.core.lang.Console;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil; import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.cell.FormulaCellValue;
import cn.hutool.poi.excel.sax.Excel03SaxReader; import cn.hutool.poi.excel.sax.Excel03SaxReader;
import cn.hutool.poi.excel.sax.handler.RowHandler; import cn.hutool.poi.excel.sax.handler.RowHandler;
import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellStyle;
@ -13,6 +15,7 @@ import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@ -104,10 +107,39 @@ public class ExcelSaxReadTest {
} }
@Test @Test
@Ignore public void formulaRead03Test() {
Console.log(FileUtil.file("data_for_sax_test.xls"));
List<Object> rows = new ArrayList<>();
ExcelUtil.readBySax("data_for_sax_test.xls", -1, (i, i1, list) -> {
if(list.size() > 1){
rows.add(list.get(1));
} else{
rows.add("");
}
});
Assert.assertEquals(50L, rows.get(3));
}
@Test
public void formulaRead07Test() {
List<Object> rows = new ArrayList<>();
ExcelUtil.readBySax("data_for_sax_test.xlsx", 0, (i, i1, list) ->
rows.add(list.get(1)));
final FormulaCellValue value = (FormulaCellValue) rows.get(3);
Assert.assertEquals(50L, value.getResult());
}
@Test
public void dateReadTest() { public void dateReadTest() {
ExcelUtil.readBySax("d:/test/date_test.xls", 0, (i, i1, list) -> List<String> rows = new ArrayList<>();
Console.log(StrUtil.join(", ", list))); ExcelUtil.readBySax("data_for_sax_test.xls", 0, (i, i1, list) ->
rows.add(StrUtil.toString(list.get(0))));
Assert.assertEquals("2020-10-09 00:00:00", rows.get(1));
// 非日期格式不做转换
Assert.assertEquals("112233", rows.get(2));
Assert.assertEquals("1000", rows.get(3));
} }
@Test @Test

Binary file not shown.

Binary file not shown.