You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Apache Commons解决CSV文件中的无效数据问题

Apache Commons CSV解析错误解决:无效字符问题

错误信息

java.lang.IllegalStateException: IOException reading next record: java.io.IOException:
(line 46196) invalid char between encapsulated token and delimiter

现有代码实现

try {
    File csvInput = getLatestFilefromDir(CSV_PATH);
    reader = new FileReader(csvInput);

    final CSVFormat csvFormat = CSVFormat.Builder.create()
            .setHeader(HEADERS)
            .setDelimiter(';')
            .setQuote('"')
            .setEscape('\\')
            .setSkipHeaderRecord(true)
            .build();

    Iterable<CSVRecord> csvRecords = csvFormat.parse(reader);

    for (CSVRecord csvRecord : csvRecords) {
        // processing
    }
} catch (Exception e) {
    log.error("Error retrieving CSV data.");
    e.printStackTrace();
}

问题数据示例

无效的CSV条目如下,问题出在转义引号的反斜杠使用错误(理想格式应为...Authorities\ \" (RGLA)...):

"TABLE_NAME";"ATTRIBUTE";"VALUE"
"SWAP_LEG_TYPE";"SWAP_LEG_TYPE_DESC";"The payments (PAY or RECEIVE) of this \"Leg\" are based on the yield linked to a specific equity or an index. (or to the actual market price of the equity or the index ???)"
"CNTPTY_TYPE";"CNTPTY_TYPE_DESC";"With Local Government we mean the so called \Regional Governments or Local Authorities\\" (RGLA) as defined by the EBA (European Banking Authority).\""

解决方案

方案1:自定义流处理(推荐)

直接全局替换字符串容易误操作,更稳妥的方式是在读取流时实时修正转义错误。通过包装FileReader为自定义Reader,精准处理异常转义序列:

try {
    File csvInput = getLatestFilefromDir(CSV_PATH);
    // 包装Reader,实时修正转义问题
    Reader correctedReader = new BufferedReader(new FileReader(csvInput)) {
        private int lastChar = -1;

        @Override
        public int read(char[] cbuf, int off, int len) throws IOException {
            int readLen = super.read(cbuf, off, len);
            if (readLen == -1) {
                return lastChar != -1 ? 1 : -1;
            }

            StringBuilder corrected = new StringBuilder();
            for (int i = off; i < off + readLen; i++) {
                char current = cbuf[i];
                if (lastChar == '\\') {
                    if (current == '"') {
                        // 保留正确的转义格式:\"
                        corrected.append('\\').append('"');
                        lastChar = -1;
                    } else if (current == '\\') {
                        // 遇到连续两个\,暂存一个,等待下一个字符判断
                        corrected.append('\\');
                        lastChar = '\\';
                    } else {
                        // \后跟非引号/非反斜杠,直接保留原字符组合
                        corrected.append('\\').append(current);
                        lastChar = -1;
                    }
                } else {
                    if (current == '\\') {
                        lastChar = '\\';
                    } else {
                        corrected.append(current);
                        lastChar = -1;
                    }
                }
            }

            // 处理最后残留的未匹配反斜杠
            if (lastChar != -1) {
                corrected.append((char) lastChar);
                lastChar = -1;
            }

            // 将修正后的内容写入输出缓冲区
            char[] result = corrected.toString().toCharArray();
            int copyLen = Math.min(result.length, len);
            System.arraycopy(result, 0, cbuf, off, copyLen);
            return copyLen;
        }
    };

    final CSVFormat csvFormat = CSVFormat.Builder.create()
            .setHeader(HEADERS)
            .setDelimiter(';')
            .setQuote('"')
            .setEscape('\\')
            .setSkipHeaderRecord(true)
            .build();

    Iterable<CSVRecord> csvRecords = csvFormat.parse(correctedReader);

    for (CSVRecord csvRecord : csvRecords) {
        // processing
    }
} catch (Exception e) {
    log.error("Error retrieving CSV data.");
    e.printStackTrace();
}

方案2:调整CSVFormat配置(有限兼容)

如果问题仅源于转义规则不匹配,可以尝试修改CSVFormat参数,但这种方式覆盖场景有限:

  • 尝试setEscape('"'):让引号自身作为转义符(即""表示一个"),但原数据中的\"会被视为普通字符,可能不符合业务预期。
  • 尝试setIgnoreSurroundingSpaces(true):仅能处理空格相关问题,对转义错误帮助不大。

方案3:预读取文件修正(临时方案)

若文件体积较小,可一次性读取全部内容,用正则修正转义后再解析:

String content = Files.readString(csvInput.toPath(), StandardCharsets.UTF_8);
// 修正\\\"为正确的\"
content = content.replaceAll("\\\\\\\\\"", "\\\\\"");
// 修正\后跟非引号的情况,确保转义符有效
content = content.replaceAll("\\\\([^\\\"])", "\\\\\\$1");
Reader reader = new StringReader(content);

注意:大文件使用此方法会占用大量内存,且正则可能误替换合法序列,仅适合小文件临时解决。

总结

优先采用方案1的自定义流处理,它在读取过程中实时修正错误,内存占用低且精准度高。小文件场景可考虑方案3,但需严格验证正则的准确性。

内容的提问来源于stack exchange,提问作者jbrezik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 22:10:28