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
列表中的第一项,我可以“正确”读入值。但是,这不是一个干净的解决方案:
- 这引出了一个问题:为什么会这样?
- 此外,我认为我们不应该解决问题而不是解决问题。只有在我的源 CSV 开头有换行符时才提供此功能。
所以我请求帮助,如何缓解这种情况?
这不是特定于 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>
我正在做一个项目,我们使用 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
列表中的第一项,我可以“正确”读入值。但是,这不是一个干净的解决方案:
- 这引出了一个问题:为什么会这样?
- 此外,我认为我们不应该解决问题而不是解决问题。只有在我的源 CSV 开头有换行符时才提供此功能。
所以我请求帮助,如何缓解这种情况?
这不是特定于 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>