我不认为你可以做很多工作来解决它。CSVParser
是一个最终类,并且不允许控制它解析底层流的方式。但是,可以通过使用自定义迭代器来解决这个问题。
public final class Csv {
private Csv() {
}
public interface ICsvParserFactory {
@Nonnull
CSVParser createCsvParser(@Nonnull Reader lineReader);
}
public static Stream<CSVRecord> tryParseLinesLeniently(final BufferedReader bufferedReader, final ICsvParserFactory csvParserFactory) {
return bufferedReader.lines()
.map(line -> {
try {
return csvParserFactory.createCsvParser(new StringReader(line))
.iterator()
.next();
} catch ( final IllegalStateException ex ) {
return null;
}
})
.filter(Objects::nonNull)
.onClose(() -> {
try {
bufferedReader.close();
} catch ( final IOException ex ) {
throw new RuntimeException(ex);
}
});
}
}
但是,无论如何,我认为这不是一个好主意:
- 它不能返回
CSVParser
实例。
- 它可能会返回一个
Iterator<CSVRecord>
而不是Stream<CSVRecord>
(并保存filter
操作),但我只是发现流更易于实现。
- 它为每一行创建一个新的 CSV 解析器,因此此方法为包含“太多”行的 CSV 文档创建许多对象。字符串阅读器可能可以重复使用。
- 该方法的整个想法是,它不是 CSV 解析器,它假设每行仅包含一行(我真的不记得 CSV/TSV 是否允许多行记录),因此它违反了 CSV 解析规则设计。它还不支持标题(但可以很容易地改进)。
final Csv.ICsvParserFactory csvParserFactory = lineReader -> {
try {
return new CSVParser(lineReader, CSVFormat.EXCEL);
} catch ( final IOException ex ) {
throw new RuntimeException(ex);
}
};
try ( final Stream<CSVRecord> csvRecords = Csv.tryParseLinesLeniently(new BufferedReader(reader), csvParserFactory) ) {
csvRecords.forEachOrdered(System.out::println);
}
如果可能,请让您的 CSV 解析器使用有效的 CSV 文档,而不是使用像这样的任何解决方法。
编辑 1
上面的代码中存在一个实现缺陷:从流返回的所有记录现在都recordNumber
设置为1
.
现在我确实相信无法使用 Apache Commons CSV 解析器修复请求,因为唯一的CSVRecord
构造函数也是包私有的,如果不使用反射或侵入其声明包,则无法在该包之外实例化。
抱歉,您要么修复了 CSV 文档,要么使用了另一个可以“更宽松”解析的解析器。