diff --git a/pom.xml b/pom.xml index 831caff..9f1a4c7 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.alibaba easyexcel - 2.2.6 + 2.2.10 jar easyexcel diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractCellValueTagHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractCellValueTagHandler.java index 3521e46..580e670 100644 --- a/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractCellValueTagHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractCellValueTagHandler.java @@ -7,6 +7,7 @@ import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; import com.alibaba.excel.util.BooleanUtils; +import com.alibaba.excel.util.StringUtils; /** * Cell Value Handler @@ -20,6 +21,7 @@ public abstract class AbstractCellValueTagHandler extends AbstractXlsxTagHandler XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder(); CellData tempCellData = xlsxReadSheetHolder.getTempCellData(); StringBuilder tempData = xlsxReadSheetHolder.getTempData(); + String tempDataString = tempData.toString(); CellDataTypeEnum oldType = tempCellData.getType(); switch (oldType) { case DIRECT_STRING: @@ -28,12 +30,20 @@ public abstract class AbstractCellValueTagHandler extends AbstractXlsxTagHandler tempCellData.setStringValue(tempData.toString()); break; case BOOLEAN: + if(StringUtils.isEmpty(tempDataString)){ + tempCellData.setType(CellDataTypeEnum.EMPTY); + break; + } tempCellData.setBooleanValue(BooleanUtils.valueOf(tempData.toString())); break; case NUMBER: case EMPTY: + if(StringUtils.isEmpty(tempDataString)){ + tempCellData.setType(CellDataTypeEnum.EMPTY); + break; + } tempCellData.setType(CellDataTypeEnum.NUMBER); - tempCellData.setNumberValue(new BigDecimal(tempData.toString())); + tempCellData.setNumberValue(BigDecimal.valueOf(Double.parseDouble(tempDataString))); break; default: throw new IllegalStateException("Cannot set values now"); diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellValueTagHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellValueTagHandler.java index 09c9264..7e685c9 100644 --- a/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellValueTagHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellValueTagHandler.java @@ -3,6 +3,7 @@ package com.alibaba.excel.analysis.v07.handlers; import com.alibaba.excel.context.xlsx.XlsxReadContext; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.util.StringUtils; /** * Cell Value Handler @@ -17,6 +18,10 @@ public class CellValueTagHandler extends AbstractCellValueTagHandler { CellData tempCellData = xlsxReadContext.xlsxReadSheetHolder().getTempCellData(); switch (tempCellData.getType()) { case STRING: + // In some cases, although cell type is a string, it may be an empty tag + if(StringUtils.isEmpty(tempCellData.getStringValue())){ + break; + } String stringValue = xlsxReadContext.readWorkbookHolder().getReadCache() .get(Integer.valueOf(tempCellData.getStringValue())); if (stringValue != null && xlsxReadContext.currentReadHolder().globalConfiguration().getAutoTrim()) { diff --git a/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java b/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java index b31bc70..693c112 100644 --- a/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java @@ -47,7 +47,7 @@ public class StringNumberConverter implements Converter { } // Excel defines formatting if (cellData.getDataFormat() != null && !StringUtils.isEmpty(cellData.getDataFormatString())) { - return NumberDataFormatterUtils.format(cellData.getNumberValue().doubleValue(), cellData.getDataFormat(), + return NumberDataFormatterUtils.format(cellData.getNumberValue(), cellData.getDataFormat(), cellData.getDataFormatString(), globalConfiguration); } // Default conversion number diff --git a/src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java b/src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java index 0bc70d1..2c955ed 100644 --- a/src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java +++ b/src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java @@ -34,6 +34,8 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.poi.ss.format.CellFormat; +import org.apache.poi.ss.format.CellFormatResult; import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.ExcelStyleDateFormatter; import org.apache.poi.ss.usermodel.FractionFormat; @@ -168,22 +170,58 @@ public class DataFormatter { this.decimalSymbols = DecimalFormatSymbols.getInstance(this.locale); } - private Format getFormat(Integer dataFormat, String dataFormatString) { + private Format getFormat(Double data,Integer dataFormat, String dataFormatString) { + + // Might be better to separate out the n p and z formats, falling back to p when n and z are not set. + // That however would require other code to be re factored. + // String[] formatBits = formatStrIn.split(";"); + // int i = cellValue > 0.0 ? 0 : cellValue < 0.0 ? 1 : 2; + // String formatStr = (i < formatBits.length) ? formatBits[i] : formatBits[0]; + String formatStr = dataFormatString; + + // Excel supports 2+ part conditional data formats, eg positive/negative/zero, + // or (>1000),(>0),(0),(negative). As Java doesn't handle these kinds + // of different formats for different ranges, just +ve/-ve, we need to + // handle these ourselves in a special way. + // For now, if we detect 2+ parts, we call out to CellFormat to handle it + // TODO Going forward, we should really merge the logic between the two classes + if (formatStr.contains(";") && + (formatStr.indexOf(';') != formatStr.lastIndexOf(';') + || rangeConditionalPattern.matcher(formatStr).matches() + ) ) { + try { + // Ask CellFormat to get a formatter for it + CellFormat cfmt = CellFormat.getInstance(locale, formatStr); + // CellFormat requires callers to identify date vs not, so do so + Object cellValueO = data; + if (DateUtil.isADateFormat(dataFormat, formatStr) && + // don't try to handle Date value 0, let a 3 or 4-part format take care of it + data.doubleValue() != 0.0) { + cellValueO = DateUtil.getJavaDate(data, use1904windowing); + } + // Wrap and return (non-cachable - CellFormat does that) + return new CellFormatResultWrapper( cfmt.apply(cellValueO) ); + } catch (Exception e) { + LOGGER.warn("Formatting failed for format {}, falling back",formatStr, e); + } + } + // See if we already have it cached - Format format = formats.get(dataFormatString); + Format format = formats.get(formatStr); if (format != null) { return format; } + // Is it one of the special built in types, General or @? - if ("General".equalsIgnoreCase(dataFormatString) || "@".equals(dataFormatString)) { + if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) { format = getDefaultFormat(); - addFormat(dataFormatString, format); + addFormat(formatStr, format); return format; } // Build a formatter, and cache it - format = createFormat(dataFormat, dataFormatString); - addFormat(dataFormatString, format); + format = createFormat(dataFormat, formatStr); + addFormat(formatStr, format); return format; } @@ -530,7 +568,7 @@ public class DataFormatter { try { return new InternalDecimalFormatWithScale(format, symbols); } catch (IllegalArgumentException iae) { - LOGGER.debug("Formatting failed for format {}, falling back", formatStr, iae); + LOGGER.error("Formatting failed for format {}, falling back", formatStr, iae); // the pattern could not be parsed correctly, // so fall back to the default number format return getDefaultFormat(); @@ -570,7 +608,7 @@ public class DataFormatter { * @return Formatted value */ private String getFormattedDateString(Double data, Integer dataFormat, String dataFormatString) { - Format dateFormat = getFormat(dataFormat, dataFormatString); + Format dateFormat = getFormat(data, dataFormat, dataFormatString); if (dateFormat instanceof ExcelStyleDateFormatter) { // Hint about the raw excel value ((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(data); @@ -592,8 +630,8 @@ public class DataFormatter { * @param dataFormatString * @return a formatted number string */ - private String getFormattedNumberString(Double data, Integer dataFormat, String dataFormatString) { - Format numberFormat = getFormat(dataFormat, dataFormatString); + private String getFormattedNumberString(BigDecimal data, Integer dataFormat, String dataFormatString) { + Format numberFormat = getFormat(data.doubleValue(), dataFormat, dataFormatString); String formatted = numberFormat.format(data); return formatted.replaceFirst("E(\\d)", "E+$1"); // to match Excel's E-notation } @@ -606,9 +644,9 @@ public class DataFormatter { * @param dataFormatString * @return */ - public String format(Double data, Integer dataFormat, String dataFormatString) { + public String format(BigDecimal data, Integer dataFormat, String dataFormatString) { if (DateUtils.isADateFormat(dataFormat, dataFormatString)) { - return getFormattedDateString(data, dataFormat, dataFormatString); + return getFormattedDateString(data.doubleValue(), dataFormat, dataFormatString); } return getFormattedNumberString(data, dataFormat, dataFormatString); } @@ -794,4 +832,26 @@ public class DataFormatter { } } + /** + * Workaround until we merge {@link org.apache.poi.ss.usermodel.DataFormatter} with {@link CellFormat}. Constant, non-cachable wrapper around a + * {@link CellFormatResult} + */ + private final class CellFormatResultWrapper extends Format { + private final CellFormatResult result; + + private CellFormatResultWrapper(CellFormatResult result) { + this.result = result; + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(result.text.trim()); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return null; // Not supported + } + } + } diff --git a/src/main/java/com/alibaba/excel/util/NumberDataFormatterUtils.java b/src/main/java/com/alibaba/excel/util/NumberDataFormatterUtils.java index 09d149b..b4ba5b0 100644 --- a/src/main/java/com/alibaba/excel/util/NumberDataFormatterUtils.java +++ b/src/main/java/com/alibaba/excel/util/NumberDataFormatterUtils.java @@ -1,5 +1,7 @@ package com.alibaba.excel.util; +import java.math.BigDecimal; + import com.alibaba.excel.metadata.format.DataFormatter; import com.alibaba.excel.metadata.GlobalConfiguration; @@ -24,7 +26,7 @@ public class NumberDataFormatterUtils { * @param globalConfiguration * @return */ - public static String format(Double data, Integer dataFormat, String dataFormatString, + public static String format(BigDecimal data, Integer dataFormat, String dataFormatString, GlobalConfiguration globalConfiguration) { DataFormatter dataFormatter = DATA_FORMATTER_THREAD_LOCAL.get(); if (dataFormatter == null) { diff --git a/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java b/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java index f1de542..d0c308a 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java @@ -4,17 +4,16 @@ import java.io.File; import java.util.List; import java.util.Locale; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; + import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.alibaba.easyexcel.test.util.TestFileUtil; -import com.alibaba.excel.EasyExcel; - /** - * * @author Jiaju Zhuang */ public class DateFormatTest { @@ -44,6 +43,14 @@ public class DateFormatTest { private void readCn(File file) { List list = EasyExcel.read(file, DateFormatData.class, null).locale(Locale.CHINA).sheet().doReadSync(); + for (DateFormatData data : list) { + if (data.getDateStringCn() != null && !data.getDateStringCn().equals(data.getDate())) { + LOGGER.info("date:cn:{},{}", data.getDateStringCn(), data.getDate()); + } + if (data.getNumberStringCn() != null && !data.getNumberStringCn().equals(data.getNumber())) { + LOGGER.info("number:cn{},{}", data.getNumberStringCn(), data.getNumber()); + } + } for (DateFormatData data : list) { Assert.assertEquals(data.getDateStringCn(), data.getDate()); Assert.assertEquals(data.getNumberStringCn(), data.getNumber()); @@ -53,6 +60,14 @@ public class DateFormatTest { private void readUs(File file) { List list = EasyExcel.read(file, DateFormatData.class, null).locale(Locale.US).sheet().doReadSync(); + for (DateFormatData data : list) { + if (data.getDateStringUs() != null && !data.getDateStringUs().equals(data.getDate())) { + LOGGER.info("date:us:{},{}", data.getDateStringUs(), data.getDate()); + } + if (data.getNumberStringUs() != null && !data.getNumberStringUs().equals(data.getNumber())) { + LOGGER.info("number:us{},{}", data.getNumberStringUs(), data.getNumber()); + } + } for (DateFormatData data : list) { Assert.assertEquals(data.getDateStringUs(), data.getDate()); Assert.assertEquals(data.getNumberStringUs(), data.getNumber()); diff --git a/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java index 375f9be..b7f63cf 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java @@ -3,21 +3,23 @@ package com.alibaba.easyexcel.test.core.encrypt; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; -import org.junit.BeforeClass; -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.runners.MethodSorters; - import com.alibaba.easyexcel.test.core.simple.SimpleData; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.support.ExcelTypeEnum; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + /** - * * @author Jiaju Zhuang */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -28,6 +30,15 @@ public class EncryptDataTest { private static File file07OutputStream; private static File file03OutputStream; + @Test + public void testformat() { + DecimalFormat decimalFormat = new DecimalFormat("0.00"); + decimalFormat.setRoundingMode(RoundingMode.HALF_UP); + BigDecimal bigDecimal = new BigDecimal("0.105"); + + System.out.println(decimalFormat.format(bigDecimal)); + } + @BeforeClass public static void init() { file07 = TestFileUtil.createNewFile("encrypt07.xlsx"); diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java b/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java index 0dcef53..9220264 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java @@ -33,9 +33,10 @@ public class Lock2Test { @Test public void test() throws Exception { - File file = TestFileUtil.readUserHomeFile("test/test6.xls"); +// File file = TestFileUtil.readUserHomeFile("test/test6.xls"); + File file = new File("/Users/zhuangjiaju/test/签到金模板-0507-v5.xlsx"); - List list = EasyExcel.read(file).sheet(1).headRowNumber(0).doReadSync(); + List list = EasyExcel.read(file).sheet(0).headRowNumber(0).doReadSync(); LOGGER.info("数据:{}", list.size()); for (Object data : list) { LOGGER.info("返回数据:{}", JSON.toJSONString(data)); diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java index 27f820b..ad09717 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java @@ -26,11 +26,7 @@ public class LockTest { public void test() throws Exception { List list = - EasyExcel.read(new FileInputStream("D:\\test\\testbug嘉惠.xlsx")).sheet().headRowNumber(0).doReadSync(); - for (Object data : list) { - LOGGER.info("返回数据:{}", JSON.toJSONString(data)); - } - list = EasyExcel.read(new File("D:\\test\\t222.xlsx")).sheet().headRowNumber(0).doReadSync(); + EasyExcel.read(new FileInputStream("/Users/zhuangjiaju/Downloads/点位配置表 (1).xlsx")).doReadAllSync(); for (Object data : list) { LOGGER.info("返回数据:{}", JSON.toJSONString(data)); } diff --git a/src/test/resources/dataformat/dataformat.xls b/src/test/resources/dataformat/dataformat.xls index 632cb1e..c8afd64 100644 Binary files a/src/test/resources/dataformat/dataformat.xls and b/src/test/resources/dataformat/dataformat.xls differ diff --git a/src/test/resources/dataformat/dataformat.xlsx b/src/test/resources/dataformat/dataformat.xlsx index 9f41f27..f77dd9f 100644 Binary files a/src/test/resources/dataformat/dataformat.xlsx and b/src/test/resources/dataformat/dataformat.xlsx differ diff --git a/update.md b/update.md index 0e377b7..f2d889a 100644 --- a/update.md +++ b/update.md @@ -1,3 +1,15 @@ +# 2.2.10 +* 修复读取的时候用string接收数字 可能四舍五入不一致的bug + +# 2.2.9 +* 修复读取的时候用string接收数字 可能四舍五入不一致的bug + +# 2.2.8 +* 兼容07在特殊的excel的情况下,读取数据异常 + +# 2.2.7 +* 修改07在特殊情况下用`String`接收数字会丢小数位的bug + # 2.2.6 * 修改跳着读取03版本空指针bug