OpenCSV 在 Java 中读取附加字节值以及第一行的第一个值

OpenCSV reads in additional byte value together with first line's first value together in Java

我正在做一个项目,我们使用 OpenCSV 读取 CSV 文件并在开始时用它们填充数据库。我注意到有一件奇怪的事情,在某些情况下无法查询给定的标识符值。在调试过程中,我发现 OpenCSV 没有正确读取 CSV。

假设我有以下 CSV 文件:

01;foo
02;bar
...

示例中的第一行也是真实CSV文件中的第一行。该文件以 UTF-8 编码。以下代码用于读入值:

 try (CSVReader csvReader = CSVUtils.createCSVReader(masterDataCSVPath, csvDelimiter)) {
            List<String[]> masterData = csvReader.readAll();
        }

创建 csvReader 的代码:

    static private CSVParser createCSVParser(String CSVDelimiter) {
        return new CSVParserBuilder().withSeparator(CSVDelimiter.charAt(0)).build();
    }

    static public CSVReader createCSVReader(String CSVPath, String CSVDelimiter) throws FileNotFoundException {
        return new CSVReaderBuilder(new FileReader(CSVPath)).withCSVParser(createCSVParser(CSVDelimiter)).build();
    }

当我使用以下代码读入 CSV 文件时,在调试过程中,我得到了 01 的以下字节值:

但是,如果我将 CSV 文件更改为(注意顶部的换行符):


01;foo
02;bar
...

读入数据变为:

在这种情况下“一切都很好”,如果我删除 masterData 列表中的第一项,我可以“正确”读入值。但是,这不是一个干净的解决方案:

所以我请求帮助,如何缓解这种情况?

这不是特定于 OpenCSV 的问题,而是 FileReader 读取 UTF 编码文件中的 BOM。这有点出乎意料,但它是有道理的,因为 FileReader 没有上下文表明它应该排除那些字节。

解决方案是手动删除它,或者 - 在我的例子中 - 使用一个库来确保它被排除在外。我编写了以下实用程序 class:

public class CSVUtils {

private static CSVParser createCSVParser(final String CSVDelimiter) {
    return new CSVParserBuilder().withSeparator(CSVDelimiter.charAt(0)).build();
}

private static  BOMInputStream versatileBOMInputStreamGenerator(final InputStream inputStream) {

    return new BOMInputStream(inputStream, ByteOrderMark.UTF_8, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_16LE,
            ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE);
}


public static  CSVReader createCSVReaderFromFile(final String CSVPath, final String CSVDelimiter) throws FileNotFoundException {
    return new CSVReaderBuilder(new InputStreamReader(
            versatileBOMInputStreamGenerator(new FileInputStream(CSVPath)), StandardCharsets.UTF_8))
            .withCSVParser(createCSVParser(CSVDelimiter)).build();
}

public static  CSVReader createCSVReaderFromString(final String content, final String CSVDelimiter) {
    byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
    return new CSVReaderBuilder(new InputStreamReader(
            versatileBOMInputStreamGenerator(new ByteArrayInputStream(contentBytes)), StandardCharsets.UTF_8))
            .withCSVParser(createCSVParser(CSVDelimiter)).build();
}

}

我所要做的就是稍后在需要时使用这些创建的 CSVReader 对象。可以看到,它使用了一些依赖,可以用

导入
import org.apache.commons.io.ByteOrderMark;
import org.apache.commons.io.input.BOMInputStream;

可以通过 POM 将这些依赖项添加到项目中,如下所示:

        <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.11.0</version>