使用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
相关产品推荐
相关产品推荐

