diff --git a/CHANGELOG.md b/CHANGELOG.md index aa38689bb..bdaad4205 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ * 【core 】 修复FileTypeUtil.getType空指针问题(issue#IAD5JM@Gitee) * 【core 】 修复IdcardUtil.isValidHKCard校验问题(issue#IAFOLI@Gitee) * 【core 】 修复Convert.digitToChinese(0)输出金额无`元整问题`(issue#3662@Github) +* 【core 】 修复CsvParser中对正文中双引号处理逻辑问题(pr#1244@Gitee) ------------------------------------------------------------------------------------------------------------- # 5.8.29(2024-07-03) diff --git a/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java b/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java index 82929414d..0820ca236 100755 --- a/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/CharSequenceUtil.java @@ -3236,7 +3236,7 @@ public class CharSequenceUtil { * @return 是否被包装 */ public static boolean isWrap(CharSequence str, char prefixChar, char suffixChar) { - if (null == str) { + if (null == str || str.length() < 2) { return false; } diff --git a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java index 48957ade5..f58a0a276 100755 --- a/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/csv/CsvParser.java @@ -281,8 +281,8 @@ public final class CsvParser extends ComputeIter implements Closeable, S } buf.mark(); addField(currentFields, currentField.toStringAndReset()); - } else if (c == config.textDelimiter) { - // 引号开始 + } else if (c == config.textDelimiter && isFieldBegin(preChar)) { + // 引号开始且出现在字段开头 inQuotes = true; copyLen++; } else if (c == CharUtil.CR) { @@ -340,8 +340,13 @@ public final class CsvParser extends ComputeIter implements Closeable, S // 忽略多余引号后的换行符 field = StrUtil.trim(field, 1, (c-> c == CharUtil.LF || c == CharUtil.CR)); - field = StrUtil.unWrap(field, textDelimiter); - field = StrUtil.replace(field, "" + textDelimiter + textDelimiter, textDelimiter + ""); + if(StrUtil.isWrap(field, textDelimiter)){ + field = StrUtil.sub(field, 1, field.length() - 1); + // https://datatracker.ietf.org/doc/html/rfc4180#section-2 + // 第七条规则,只有包装内的包装符需要转义 + field = StrUtil.replace(field, String.valueOf(textDelimiter) + textDelimiter, String.valueOf(textDelimiter)); + } + if(this.config.trimField){ // issue#I49M0C@Gitee field = StrUtil.trim(field); @@ -361,6 +366,24 @@ public final class CsvParser extends ComputeIter implements Closeable, S return (c == CharUtil.CR || c == CharUtil.LF) && preChar != CharUtil.CR; } + /** + * 通过前一个字符,判断是否字段开始,几种情况: + *
    + *
  • 正文开头,无前字符
  • + *
  • 字段分隔符,即上个字段结束
  • + *
  • 换行符,即新行开始
  • + *
+ * + * @param preChar 前字符 + * @return 是否字段开始 + */ + private boolean isFieldBegin(final int preChar) { + return preChar == -1 + || preChar == config.fieldSeparator + || preChar == CharUtil.LF + || preChar == CharUtil.CR; + } + /** * 内部Buffer * diff --git a/hutool-core/src/test/java/cn/hutool/core/text/csv/Pr1244Test.java b/hutool-core/src/test/java/cn/hutool/core/text/csv/Pr1244Test.java index 37e8b35e5..5255cb4bf 100644 --- a/hutool-core/src/test/java/cn/hutool/core/text/csv/Pr1244Test.java +++ b/hutool-core/src/test/java/cn/hutool/core/text/csv/Pr1244Test.java @@ -13,7 +13,7 @@ import static org.junit.Assert.assertEquals; public class Pr1244Test { @Test public void csvReadTest() { - final String csv = "a,q\"\"e,d,f"; + final String csv = "a,q\"e,d,f"; final CsvReader reader = CsvUtil.getReader(new StringReader(csv)); final CsvData read = reader.read(); assertEquals(4, read.getRow(0).size());