首页 > 解决方案 > OpenCSV 在 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 数据错误

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


01;foo
02;bar
...

读入的数据变为:

以换行符开头的 CSV 数据

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

所以我恳请帮助,如何减轻这种情况?

标签: javaspringcsvcharacter-encodingopencsv

解决方案


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

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

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>

推荐阅读