diff --git a/fastexcel/src/main/java/cn/idev/excel/analysis/csv/CsvExcelReadExecutor.java b/fastexcel/src/main/java/cn/idev/excel/analysis/csv/CsvExcelReadExecutor.java index d3954ee3d..20ae30708 100644 --- a/fastexcel/src/main/java/cn/idev/excel/analysis/csv/CsvExcelReadExecutor.java +++ b/fastexcel/src/main/java/cn/idev/excel/analysis/csv/CsvExcelReadExecutor.java @@ -146,18 +146,21 @@ private CSVParser csvParser() throws IOException { // As a fallback, build the CSV parser using the input stream. return buildCsvParser(csvFormat, csvReadWorkbookHolder.getInputStream(), byteOrderMark); } + /** * Builds and returns a CSVParser instance based on the provided CSVFormat, InputStream, and ByteOrderMarkEnum. * - * @param csvFormat The format configuration for parsing the CSV file. - * @param inputStream The input stream from which the CSV data will be read. - * @param byteOrderMark The enumeration representing the Byte Order Mark (BOM) of the file's character set. - * @return A CSVParser instance configured to parse the CSV data. - * @throws IOException If an I/O error occurs while creating the parser or reading from the input stream. - * + *

* This method checks if the byteOrderMark is null. If it is null, it creates a CSVParser using the provided * input stream and charset. Otherwise, it wraps the input stream with a BOMInputStream to handle files with a * Byte Order Mark, ensuring proper decoding of the file content. + *

+ * + * @param csvFormat The format configuration for parsing the CSV file. + * @param inputStream The input stream from which the CSV data will be read. + * @param byteOrderMark The enumeration representing the Byte Order Mark (BOM) of the file's character set. + * @return A CSVParser instance configured to parse the CSV data. + * @throws IOException If an I/O error occurs while creating the parser or reading from the input stream. */ private CSVParser buildCsvParser(CSVFormat csvFormat, InputStream inputStream, ByteOrderMarkEnum byteOrderMark) throws IOException { @@ -173,26 +176,28 @@ private CSVParser buildCsvParser(CSVFormat csvFormat, InputStream inputStream, B /** * Processes a single CSV record and maps its content to a structured format for further analysis. * - * @param record The CSV record to be processed. + * @param record The CSV record to be processed. * @param rowIndex The index of the current row being processed. - * This method performs the following steps: - * 1. Initializes a `LinkedHashMap` to store cell data, ensuring the order of columns is preserved. - * 2. Iterates through each cell in the CSV record using an iterator. - * 3. For each cell, creates a `ReadCellData` object and sets its metadata (row index, column index, type, and value). - * - If the cell is not blank, it is treated as a string and optionally trimmed based on the `autoTrim` configuration. - * - If the cell is blank, it is marked as empty. - * 4. Adds the processed cell data to the `cellMap`. - * 5. Determines the row type: if the `cellMap` is empty, the row is marked as `EMPTY`; otherwise, it is marked as `DATA`. - * 6. Creates a `ReadRowHolder` object with the row's metadata and cell map, and stores it in the context. - * 7. Updates the context's sheet holder with the cell map and row index. - * 8. Notifies the analysis event processor that the row processing has ended. + * This method performs the following steps: + * 1. Initializes a `LinkedHashMap` to store cell data, ensuring the order of columns is preserved. + * 2. Iterates through each cell in the CSV record using an iterator. + * 3. For each cell, creates a `ReadCellData` object and sets its metadata (row index, column index, type, and value). + * - If the cell is not blank, it is treated as a string and optionally trimmed based on the `autoTrim` configuration. + * - If the cell is blank, it is marked as empty. + * 4. Adds the processed cell data to the `cellMap`. + * 5. Determines the row type: if the `cellMap` is empty, the row is marked as `EMPTY`; otherwise, it is marked as `DATA`. + * 6. Creates a `ReadRowHolder` object with the row's metadata and cell map, and stores it in the context. + * 7. Updates the context's sheet holder with the cell map and row index. + * 8. Notifies the analysis event processor that the row processing has ended. */ private void dealRecord(CSVRecord record, int rowIndex) { Map cellMap = new LinkedHashMap<>(); Iterator cellIterator = record.iterator(); int columnIndex = 0; Boolean autoTrim = - csvReadContext.currentReadHolder().globalConfiguration().getAutoTrim(); + csvReadContext.csvReadWorkbookHolder().globalConfiguration().getAutoTrim(); + Boolean autoStrip = + csvReadContext.csvReadWorkbookHolder().globalConfiguration().getAutoStrip(); while (cellIterator.hasNext()) { String cellString = cellIterator.next(); ReadCellData readCellData = new ReadCellData<>(); @@ -202,7 +207,13 @@ private void dealRecord(CSVRecord record, int rowIndex) { // csv is an empty string of whether ,, is read or ,"", if (StringUtils.isNotBlank(cellString)) { readCellData.setType(CellDataTypeEnum.STRING); - readCellData.setStringValue(autoTrim ? cellString.trim() : cellString); + if (autoStrip) { + readCellData.setStringValue(StringUtils.strip(cellString)); + } else if (autoTrim) { + readCellData.setStringValue(cellString.trim()); + } else { + readCellData.setStringValue(cellString); + } } else { readCellData.setType(CellDataTypeEnum.EMPTY); } diff --git a/fastexcel/src/main/java/cn/idev/excel/analysis/v03/handlers/LabelRecordHandler.java b/fastexcel/src/main/java/cn/idev/excel/analysis/v03/handlers/LabelRecordHandler.java index 94c3d4d5c..cf2ace9ed 100644 --- a/fastexcel/src/main/java/cn/idev/excel/analysis/v03/handlers/LabelRecordHandler.java +++ b/fastexcel/src/main/java/cn/idev/excel/analysis/v03/handlers/LabelRecordHandler.java @@ -3,7 +3,9 @@ import cn.idev.excel.analysis.v03.IgnorableXlsRecordHandler; import cn.idev.excel.context.xls.XlsReadContext; import cn.idev.excel.enums.RowTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.util.StringUtils; import org.apache.poi.hssf.record.LabelRecord; import org.apache.poi.hssf.record.Record; @@ -15,9 +17,14 @@ public class LabelRecordHandler extends AbstractXlsRecordHandler implements Igno public void processRecord(XlsReadContext xlsReadContext, Record record) { LabelRecord lrec = (LabelRecord) record; String data = lrec.getValue(); - if (data != null - && xlsReadContext.currentReadHolder().globalConfiguration().getAutoTrim()) { - data = data.trim(); + if (data != null) { + GlobalConfiguration globalConfiguration = + xlsReadContext.currentReadHolder().globalConfiguration(); + if (globalConfiguration.getAutoStrip()) { + data = StringUtils.strip(data); + } else if (globalConfiguration.getAutoTrim()) { + data = data.trim(); + } } xlsReadContext .xlsReadSheetHolder() diff --git a/fastexcel/src/main/java/cn/idev/excel/analysis/v03/handlers/LabelSstRecordHandler.java b/fastexcel/src/main/java/cn/idev/excel/analysis/v03/handlers/LabelSstRecordHandler.java index 3e272bf6a..ef71ada72 100644 --- a/fastexcel/src/main/java/cn/idev/excel/analysis/v03/handlers/LabelSstRecordHandler.java +++ b/fastexcel/src/main/java/cn/idev/excel/analysis/v03/handlers/LabelSstRecordHandler.java @@ -5,7 +5,9 @@ import cn.idev.excel.context.xls.XlsReadContext; import cn.idev.excel.enums.RowTypeEnum; import cn.idev.excel.metadata.Cell; +import cn.idev.excel.metadata.GlobalConfiguration; import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.util.StringUtils; import java.util.Map; import org.apache.poi.hssf.record.LabelSSTRecord; import org.apache.poi.hssf.record.Record; @@ -31,7 +33,12 @@ public void processRecord(XlsReadContext xlsReadContext, Record record) { (int) lsrec.getColumn(), ReadCellData.newEmptyInstance(lsrec.getRow(), (int) lsrec.getColumn())); return; } - if (xlsReadContext.currentReadHolder().globalConfiguration().getAutoTrim()) { + + GlobalConfiguration globalConfiguration = + xlsReadContext.currentReadHolder().globalConfiguration(); + if (globalConfiguration.getAutoStrip()) { + data = StringUtils.strip(data); + } else if (globalConfiguration.getAutoTrim()) { data = data.trim(); } cellMap.put((int) lsrec.getColumn(), ReadCellData.newInstance(data, lsrec.getRow(), (int) lsrec.getColumn())); diff --git a/fastexcel/src/main/java/cn/idev/excel/analysis/v07/handlers/CellTagHandler.java b/fastexcel/src/main/java/cn/idev/excel/analysis/v07/handlers/CellTagHandler.java index a6b7ded89..6c850060b 100644 --- a/fastexcel/src/main/java/cn/idev/excel/analysis/v07/handlers/CellTagHandler.java +++ b/fastexcel/src/main/java/cn/idev/excel/analysis/v07/handlers/CellTagHandler.java @@ -4,6 +4,7 @@ import cn.idev.excel.constant.FastExcelConstants; import cn.idev.excel.context.xlsx.XlsxReadContext; import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; import cn.idev.excel.metadata.data.ReadCellData; import cn.idev.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; import cn.idev.excel.util.BooleanUtils; @@ -95,9 +96,14 @@ public void endElement(XlsxReadContext xlsxReadContext, String name) { throw new IllegalStateException("Cannot set values now"); } - if (tempCellData.getStringValue() != null - && xlsxReadContext.currentReadHolder().globalConfiguration().getAutoTrim()) { - tempCellData.setStringValue(tempCellData.getStringValue().trim()); + if (tempCellData.getStringValue() != null) { + GlobalConfiguration globalConfiguration = + xlsxReadContext.currentReadHolder().globalConfiguration(); + if (globalConfiguration.getAutoStrip()) { + tempCellData.setStringValue(StringUtils.strip(tempCellData.getStringValue())); + } else if (globalConfiguration.getAutoTrim()) { + tempCellData.setStringValue(tempCellData.getStringValue().trim()); + } } tempCellData.checkEmpty(); diff --git a/fastexcel/src/main/java/cn/idev/excel/metadata/AbstractHolder.java b/fastexcel/src/main/java/cn/idev/excel/metadata/AbstractHolder.java index cd51693b2..68b3a1600 100644 --- a/fastexcel/src/main/java/cn/idev/excel/metadata/AbstractHolder.java +++ b/fastexcel/src/main/java/cn/idev/excel/metadata/AbstractHolder.java @@ -65,6 +65,15 @@ public AbstractHolder(BasicParameter basicParameter, AbstractHolder prentAbstrac globalConfiguration.setAutoTrim(basicParameter.getAutoTrim()); } + if (basicParameter.getAutoStrip() == null) { + if (prentAbstractHolder != null) { + globalConfiguration.setAutoStrip( + prentAbstractHolder.getGlobalConfiguration().getAutoStrip()); + } + } else { + globalConfiguration.setAutoStrip(basicParameter.getAutoStrip()); + } + if (basicParameter.getUse1904windowing() == null) { if (prentAbstractHolder != null) { globalConfiguration.setUse1904windowing( diff --git a/fastexcel/src/main/java/cn/idev/excel/metadata/AbstractParameterBuilder.java b/fastexcel/src/main/java/cn/idev/excel/metadata/AbstractParameterBuilder.java index 6c9ec93c9..8a79e0fb0 100644 --- a/fastexcel/src/main/java/cn/idev/excel/metadata/AbstractParameterBuilder.java +++ b/fastexcel/src/main/java/cn/idev/excel/metadata/AbstractParameterBuilder.java @@ -58,7 +58,7 @@ public T registerConverter(Converter converter) { /** * true if date uses 1904 windowing, or false if using 1900 date windowing. - * + *

* default is false * * @param use1904windowing @@ -83,7 +83,7 @@ public T locale(Locale locale) { /** * The cache used when parsing fields such as head. - * + *

* default is THREAD_LOCAL. * * @since 3.3.0 @@ -104,6 +104,17 @@ public T autoTrim(Boolean autoTrim) { return self(); } + /** + * Automatic strip includes sheet name and content + * + * @param autoStrip + * @return + */ + public T autoStrip(Boolean autoStrip) { + parameter().setAutoStrip(autoStrip); + return self(); + } + @SuppressWarnings("unchecked") protected T self() { return (T) this; diff --git a/fastexcel/src/main/java/cn/idev/excel/metadata/BasicParameter.java b/fastexcel/src/main/java/cn/idev/excel/metadata/BasicParameter.java index 74ebb8ff4..fea08320b 100644 --- a/fastexcel/src/main/java/cn/idev/excel/metadata/BasicParameter.java +++ b/fastexcel/src/main/java/cn/idev/excel/metadata/BasicParameter.java @@ -60,4 +60,8 @@ public class BasicParameter { * default is THREAD_LOCAL. */ private CacheLocationEnum filedCacheLocation; + /** + * Automatic strip includes sheet name and content + */ + private Boolean autoStrip; } diff --git a/fastexcel/src/main/java/cn/idev/excel/metadata/GlobalConfiguration.java b/fastexcel/src/main/java/cn/idev/excel/metadata/GlobalConfiguration.java index ba1227ec5..158be7ec6 100644 --- a/fastexcel/src/main/java/cn/idev/excel/metadata/GlobalConfiguration.java +++ b/fastexcel/src/main/java/cn/idev/excel/metadata/GlobalConfiguration.java @@ -23,8 +23,6 @@ public class GlobalConfiguration { * true if date uses 1904 windowing, or false if using 1900 date windowing. * * default is false - * - * @return */ private Boolean use1904windowing; /** @@ -47,8 +45,16 @@ public class GlobalConfiguration { */ private CacheLocationEnum filedCacheLocation; + /** + * Automatic strip includes sheet name and content + * + * default is false + */ + private Boolean autoStrip; + public GlobalConfiguration() { this.autoTrim = Boolean.TRUE; + this.autoStrip = Boolean.FALSE; this.use1904windowing = Boolean.FALSE; this.locale = Locale.getDefault(); this.useScientificFormat = Boolean.FALSE; diff --git a/fastexcel/src/main/java/cn/idev/excel/read/builder/CsvReaderBuilder.java b/fastexcel/src/main/java/cn/idev/excel/read/builder/CsvReaderBuilder.java index fd6096632..0dc767e7a 100644 --- a/fastexcel/src/main/java/cn/idev/excel/read/builder/CsvReaderBuilder.java +++ b/fastexcel/src/main/java/cn/idev/excel/read/builder/CsvReaderBuilder.java @@ -107,9 +107,9 @@ public CsvReaderBuilder escape(Character escape) { } private ExcelReader buildExcelReader() { - if (this.readWorkbook.getAutoTrim() != null) { - this.csvFormatBuilder.setTrim(this.readWorkbook.getAutoTrim()); - } + this.csvFormatBuilder.setTrim(this.readWorkbook.getAutoTrim() == null + || this.readWorkbook.getAutoTrim() + || Boolean.TRUE.equals(this.readWorkbook.getAutoStrip())); if (this.readWorkbook.getIgnoreEmptyRow() != null) { this.csvFormatBuilder.setIgnoreEmptyLines(this.readWorkbook.getIgnoreEmptyRow()); } diff --git a/fastexcel/src/main/java/cn/idev/excel/read/metadata/ReadSheet.java b/fastexcel/src/main/java/cn/idev/excel/read/metadata/ReadSheet.java index 0780c94d9..e821dcfa1 100644 --- a/fastexcel/src/main/java/cn/idev/excel/read/metadata/ReadSheet.java +++ b/fastexcel/src/main/java/cn/idev/excel/read/metadata/ReadSheet.java @@ -96,6 +96,7 @@ public void copyBasicParameter(ReadSheet other) { this.setClazz(other.getClazz()); this.setCustomConverterList(other.getCustomConverterList()); this.setAutoTrim(other.getAutoTrim()); + this.setAutoStrip(other.getAutoStrip()); this.setUse1904windowing(other.getUse1904windowing()); this.setNumRows(other.getNumRows()); this.setHidden(other.isHidden()); diff --git a/fastexcel/src/main/java/cn/idev/excel/read/processor/DefaultAnalysisEventProcessor.java b/fastexcel/src/main/java/cn/idev/excel/read/processor/DefaultAnalysisEventProcessor.java index ba793d033..100204b62 100644 --- a/fastexcel/src/main/java/cn/idev/excel/read/processor/DefaultAnalysisEventProcessor.java +++ b/fastexcel/src/main/java/cn/idev/excel/read/processor/DefaultAnalysisEventProcessor.java @@ -166,9 +166,15 @@ private void buildHead(AnalysisContext analysisContext, MapThis is similar to {@link String#trim()} but removes whitespace. + * Whitespace is defined by {@link #isBlankChar(char)}.

+ * + *

A {@code null} input String returns {@code null}.

+ * + *
+     * StringUtils.strip(null)     = null
+     * StringUtils.strip("")       = ""
+     * StringUtils.strip("   ")    = ""
+     * StringUtils.strip("abc")    = "abc"
+     * StringUtils.strip("  abc")  = "abc"
+     * StringUtils.strip("abc  ")  = "abc"
+     * StringUtils.strip(" abc ")  = "abc"
+     * StringUtils.strip(" ab c ") = "ab c"
+     * 
+ * + * @param str the String to remove whitespace from, may be null + * @return the stripped String, {@code null} if null String input + */ + public static String strip(final String str) { + if (str == null || str.isEmpty()) { + return str; + } + + int beginIndex = 0; + int endIndex = str.length() - 1; + + while (beginIndex <= endIndex && isBlankChar(str.charAt(beginIndex))) { + beginIndex++; + } + + while (endIndex > beginIndex && isBlankChar(str.charAt(endIndex))) { + endIndex--; + } + + return str.substring(beginIndex, endIndex + 1); + } + + /** + * Checks if the character is whitespace + * + * @param ch the character to check + * @return {@code true} if the character is a Java whitespace + * character; {@code false} otherwise. + * @see java.lang.Character#isWhitespace(char) + * @see java.lang.Character#isSpaceChar(char) + */ + public static boolean isBlankChar(final char ch) { + return Character.isSpaceChar(ch) + || Character.isWhitespace(ch) + || ch == '\u202a' + || ch == '\ufeff' + || ch == '\u3164' + || ch == '\u2800' + || ch == '\u200c' + || ch == '\u180e'; + } } diff --git a/fastexcel/src/main/java/cn/idev/excel/write/builder/CsvWriterBuilder.java b/fastexcel/src/main/java/cn/idev/excel/write/builder/CsvWriterBuilder.java index 549609fcc..ef95eb086 100644 --- a/fastexcel/src/main/java/cn/idev/excel/write/builder/CsvWriterBuilder.java +++ b/fastexcel/src/main/java/cn/idev/excel/write/builder/CsvWriterBuilder.java @@ -107,9 +107,9 @@ public CsvWriterBuilder escape(Character escape) { } private ExcelWriter buildExcelWriter() { - if (this.writeWorkbook.getAutoTrim() != null) { - this.csvFormatBuilder.setTrim(this.writeWorkbook.getAutoTrim()); - } + this.csvFormatBuilder.setTrim(this.writeWorkbook.getAutoTrim() == null + || this.writeWorkbook.getAutoTrim() + || Boolean.TRUE.equals(this.writeWorkbook.getAutoStrip())); if (this.writeWorkbook.getNeedHead() != null) { this.csvFormatBuilder.setSkipHeaderRecord(!this.writeWorkbook.getNeedHead()); } diff --git a/fastexcel/src/test/java/cn/idev/excel/parameter/AutoStripParameterTest.java b/fastexcel/src/test/java/cn/idev/excel/parameter/AutoStripParameterTest.java new file mode 100644 index 000000000..d88f13716 --- /dev/null +++ b/fastexcel/src/test/java/cn/idev/excel/parameter/AutoStripParameterTest.java @@ -0,0 +1,199 @@ +package cn.idev.excel.parameter; + +import cn.idev.excel.ExcelReader; +import cn.idev.excel.ExcelWriter; +import cn.idev.excel.FastExcel; +import cn.idev.excel.context.AnalysisContext; +import cn.idev.excel.event.AnalysisEventListener; +import cn.idev.excel.read.metadata.ReadSheet; +import cn.idev.excel.support.ExcelTypeEnum; +import cn.idev.excel.util.ParameterUtil; +import cn.idev.excel.util.SheetUtils; +import cn.idev.excel.util.StringUtils; +import cn.idev.excel.util.TestFileUtil; +import cn.idev.excel.write.metadata.WriteSheet; +import com.alibaba.fastjson2.JSON; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +@Slf4j +public class AutoStripParameterTest { + + private static File testFile; + private static final String FW_SPACES = " "; + private static final String SPACES = " "; + + @Test + public void test03() { + testAutoStripSheetNameInternal(ExcelTypeEnum.XLS, null, null); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLS, null, false); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLS, null, true); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLS, false, null); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLS, false, false); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLS, false, true); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLS, true, null); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLS, true, false); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLS, true, true); + + testAutoStripContentInternal(ExcelTypeEnum.XLS, null, null); + testAutoStripContentInternal(ExcelTypeEnum.XLS, null, false); + testAutoStripContentInternal(ExcelTypeEnum.XLS, null, true); + testAutoStripContentInternal(ExcelTypeEnum.XLS, false, null); + testAutoStripContentInternal(ExcelTypeEnum.XLS, false, false); + testAutoStripContentInternal(ExcelTypeEnum.XLS, false, true); + testAutoStripContentInternal(ExcelTypeEnum.XLS, true, null); + testAutoStripContentInternal(ExcelTypeEnum.XLS, true, false); + testAutoStripContentInternal(ExcelTypeEnum.XLS, true, true); + } + + @Test + public void test07() { + testAutoStripSheetNameInternal(ExcelTypeEnum.XLSX, null, null); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLSX, null, false); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLSX, null, true); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLSX, false, null); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLSX, false, false); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLSX, false, true); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLSX, true, null); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLSX, true, false); + testAutoStripSheetNameInternal(ExcelTypeEnum.XLSX, true, true); + + testAutoStripContentInternal(ExcelTypeEnum.XLSX, null, null); + testAutoStripContentInternal(ExcelTypeEnum.XLSX, null, false); + testAutoStripContentInternal(ExcelTypeEnum.XLSX, null, true); + testAutoStripContentInternal(ExcelTypeEnum.XLSX, false, null); + testAutoStripContentInternal(ExcelTypeEnum.XLSX, false, false); + testAutoStripContentInternal(ExcelTypeEnum.XLSX, false, true); + testAutoStripContentInternal(ExcelTypeEnum.XLSX, true, null); + testAutoStripContentInternal(ExcelTypeEnum.XLSX, true, false); + testAutoStripContentInternal(ExcelTypeEnum.XLSX, true, true); + } + + @Test + public void testCSV() { + testAutoStripContentInternal(ExcelTypeEnum.CSV, null, null); + testAutoStripContentInternal(ExcelTypeEnum.CSV, null, false); + testAutoStripContentInternal(ExcelTypeEnum.CSV, null, true); + testAutoStripContentInternal(ExcelTypeEnum.CSV, false, null); + testAutoStripContentInternal(ExcelTypeEnum.CSV, false, false); + testAutoStripContentInternal(ExcelTypeEnum.CSV, false, true); + testAutoStripContentInternal(ExcelTypeEnum.CSV, true, null); + testAutoStripContentInternal(ExcelTypeEnum.CSV, true, false); + testAutoStripContentInternal(ExcelTypeEnum.CSV, true, true); + } + + private void testAutoStripSheetNameInternal( + final ExcelTypeEnum excelType, final Boolean autoTrim, final Boolean autoStrip) { + testFile = TestFileUtil.createNewFile("auto-strip-sheet-name" + excelType.getValue()); + + final String sheetNameSpaces = SPACES + "Sheet1" + SPACES; + final String sheetNameFullWidthSpaces = FW_SPACES + "Sheet2" + FW_SPACES; + + List demoList = new ArrayList<>(); + ParameterData simpleData = new ParameterData(); + simpleData.setName("string"); + demoList.add(simpleData); + + try (ExcelWriter excelWriter = FastExcel.write(testFile, ParameterData.class) + .excelType(excelType) + .autoTrim(autoTrim) + .autoStrip(autoStrip) + .build()) { + WriteSheet writeSheet = FastExcel.writerSheet(sheetNameSpaces).build(); + excelWriter.write(demoList, writeSheet); + writeSheet = FastExcel.writerSheet(sheetNameFullWidthSpaces).build(); + excelWriter.write(demoList, writeSheet); + } + + try (ExcelReader excelReader = FastExcel.read(testFile) + .excelType(excelType) + .head(ParameterData.class) + .registerReadListenerIfNotNull(new AnalysisEventListener() { + @Override + public void invoke(ParameterData data, AnalysisContext context) { + log.info("Read one record: {}", JSON.toJSONString(data)); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + // global configuration match + Assertions.assertEquals( + autoTrim == null ? Boolean.TRUE : autoTrim, + ParameterUtil.getAutoTrimFlag( + context.readSheetHolder().getReadSheet(), context)); + Assertions.assertEquals( + autoStrip == null ? Boolean.FALSE : autoStrip, + ParameterUtil.getAutoStripFlag( + context.readSheetHolder().getReadSheet(), context)); + + // sheet name match + ReadSheet readSheet = context.readSheetHolder().getReadSheet(); + Assertions.assertEquals(readSheet, SheetUtils.match(readSheet, context)); + } + }) + .autoTrim(autoTrim) + .autoStrip(autoStrip) + .build()) { + + // set sheet name + excelReader.read( + FastExcel.readSheet(sheetNameSpaces).build(), + FastExcel.readSheet(sheetNameFullWidthSpaces).build()); + } + } + + private void testAutoStripContentInternal( + final ExcelTypeEnum excelType, final Boolean autoTrim, final Boolean autoStrip) { + testFile = TestFileUtil.createNewFile("auto-strip-content" + excelType.getValue()); + + final String testContentSpaces = SPACES + "String Data1" + SPACES; + final String testContentFullWidthSpaces = FW_SPACES + "String Data2" + FW_SPACES; + + List demoList = new ArrayList<>(); + ParameterData simpleData = new ParameterData(); + // normal spaces + simpleData.setName(testContentSpaces); + demoList.add(simpleData); + + simpleData = new ParameterData(); + // full-width spaces + simpleData.setName(testContentFullWidthSpaces); + demoList.add(simpleData); + + FastExcel.write(testFile, ParameterData.class) + .excelType(excelType) + .autoTrim(autoTrim) + .autoStrip(autoStrip) + .sheet() + .doWrite(demoList); + + List dataList = FastExcel.read(testFile) + .excelType(excelType) + .head(ParameterData.class) + .autoTrim(autoTrim) + .autoStrip(autoStrip) + .sheet() + .doReadSync(); + + log.info("Read records: {}", JSON.toJSONString(dataList)); + Assertions.assertEquals(2, dataList.size()); + if (Boolean.TRUE.equals(autoStrip)) { + Assertions.assertEquals( + StringUtils.strip(testContentSpaces), dataList.get(0).getName()); + Assertions.assertEquals( + StringUtils.strip(testContentFullWidthSpaces), + dataList.get(1).getName()); + } else if (autoTrim == null || autoTrim) { + Assertions.assertEquals(testContentSpaces.trim(), dataList.get(0).getName()); + Assertions.assertEquals( + testContentFullWidthSpaces.trim(), dataList.get(1).getName()); + } else { + Assertions.assertEquals(testContentSpaces, dataList.get(0).getName()); + Assertions.assertEquals(testContentFullWidthSpaces, dataList.get(1).getName()); + } + } +} diff --git a/fastexcel/src/test/java/cn/idev/excel/util/StringUtilsTest.java b/fastexcel/src/test/java/cn/idev/excel/util/StringUtilsTest.java new file mode 100644 index 000000000..0e2e1f677 --- /dev/null +++ b/fastexcel/src/test/java/cn/idev/excel/util/StringUtilsTest.java @@ -0,0 +1,34 @@ +package cn.idev.excel.util; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class StringUtilsTest { + + @Test + void stripTest() { + Assertions.assertNull(StringUtils.strip(null)); + Assertions.assertEquals("", StringUtils.strip("")); + Assertions.assertEquals("", StringUtils.strip(" ")); + Assertions.assertEquals("abc", StringUtils.strip("abc")); + Assertions.assertEquals("abc", StringUtils.strip(" abc")); + Assertions.assertEquals("abc", StringUtils.strip("abc ")); + Assertions.assertEquals("abc", StringUtils.strip(" abc ")); + Assertions.assertEquals("abc", StringUtils.strip(" abc ")); + Assertions.assertEquals("abc", StringUtils.strip(" abc ")); + Assertions.assertEquals("ab c", StringUtils.strip(" ab c ")); + Assertions.assertEquals("ab c", StringUtils.strip(" ab c ")); + } + + @Test + void isBlankCharTest() { + Assertions.assertTrue(StringUtils.isBlankChar(' ')); + Assertions.assertTrue(StringUtils.isBlankChar(' ')); + Assertions.assertTrue(StringUtils.isBlankChar('\ufeff')); + Assertions.assertTrue(StringUtils.isBlankChar('\u202a')); + Assertions.assertTrue(StringUtils.isBlankChar('\u3164')); + Assertions.assertTrue(StringUtils.isBlankChar('\u2800')); + Assertions.assertTrue(StringUtils.isBlankChar('\u200c')); + Assertions.assertTrue(StringUtils.isBlankChar('\u180e')); + } +}