由于 CSV 差异 Before/After 保存导致解析错误(Java w/ Apache Commons CSV)
Error Parsing due to CSV Differences Before/After Saving (Java w/ Apache Commons CSV)
我有一个 37 列的 CSV 文件,我正在 Java 中使用 Apache Commons CSV 1.2 进行解析。我的设置代码如下:
//initialize FileReader object
FileReader fileReader = new FileReader(file);
//intialize CSVFormat object
CSVFormat csvFileFormat = CSVFormat.DEFAULT.withHeader(FILE_HEADER_MAPPING);
//initialize CSVParser object
CSVParser csvFileParser = new CSVParser(fileReader, csvFileFormat);
//Get a list of CSV file records
List<CSVRecord> csvRecords = csvFileParser.getRecords();
// process accordingly
我的问题是,当我将要处理的 CSV 复制到我的目标目录和 运行 我的解析程序时,出现以下错误:
Exception in thread "main" java.lang.IllegalArgumentException: Index for header 'Title' is 7 but CSVRecord only has 6 values!
at org.apache.commons.csv.CSVRecord.get(CSVRecord.java:110)
at launcher.QualysImport.createQualysRecords(Unknown Source)
at launcher.QualysImport.importQualysRecords(Unknown Source)
at launcher.Main.main(Unknown Source)
但是,如果我将文件复制到我的目标目录,打开并保存它,然后再次尝试该程序,它就可以运行。打开并保存 CSV 文件会在末尾添加所需的逗号,这样我的程序就不会抱怨没有足够的 headers 来阅读。
对于上下文,这是 before/after 保存的示例行:
之前(失败):"data"、"data"、"data"、"data"
(工作后):"data","data","data","data",
所以我的问题是:为什么 CSV 格式在我打开和保存时会发生变化?我没有更改任何值或编码,并且 MS-DOS 或常规 .csv 格式在保存时的行为相同。另外,我在测试中使用 Excel 到 copy/open/save。
我需要使用一些编码或格式设置吗?我可以通过编程方式解决这个问题吗?
提前致谢!
编辑#1:
对于其他上下文,当我第一次查看原始文件中的空行时,它只有换行符 ^M,如下所示:
^M
在 Excel 中打开并保存后,我的所有 37 个空白字段看起来像这样:
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,^M
这是 Windows 编码差异吗?
也许试试这个:
为给定的文件创建一个解析器。
parse(File 文件, Charset 字符集, CSVFormat 格式)
//导入导入java.nio.charset.StandardCharsets;
//StandardCharsets.UTF_8
注意:此方法使用 FileReader.FileReader(java.io.File) 在内部创建一个 FileReader,它又依赖于执行代码的 JVM 的默认编码。
或者尝试使用 AllowMissingColumnNames?
//intialize CSVFormat object
CSVFormat csvFileFormat = CSVFormat.DEFAULT.withHeader(FILE_HEADER_MAPPING).withAllowMissingColumnNames();
也许这是与最初生成文件的任何内容的兼容性问题。似乎 Excel 接受空行作为每列中包含空字符串的有效行,列数与其他行相匹配。然后它根据带有列分隔符的 CSV 约定保存它。
(^M 是 Carriage Return 字符;在 Microsoft 系统中,它位于文本文件行尾的 Line Feed 字符之前)
也许您可以通过创建自己的 Reader 子类来处理它,使其位于 FileReader 和 CSVParser 之间。您的 reader 将读取一行,如果它是空白的,则 return 一行包含正确数量的逗号。否则只是 return 该行原样。
例如:
class MyCSVCompatibilityReader extends BufferedReader
{
private final BufferedReader delegate;
public MyCSVCompatibilityReader(final FileReader fileReader)
{
this.delegate = new BufferedReader(fileReader);
}
@Override
public String readLine()
{
final String line = this.delegate.readLine();
if ("".equals(line.trim())
{ return ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"; }
else
{ return line; }
}
}
实现接口时还有很多其他细节需要正确实现。您需要传递对所有其他方法(关闭、就绪、重置、跳过等)的调用,并确保每个 read()
方法都能正常工作。如果文件很容易放入内存,那么只读取文件并将固定版本写入 CSVParser 的新 StringWriter then create a StringReader 可能会更容易。
我有一个 37 列的 CSV 文件,我正在 Java 中使用 Apache Commons CSV 1.2 进行解析。我的设置代码如下:
//initialize FileReader object
FileReader fileReader = new FileReader(file);
//intialize CSVFormat object
CSVFormat csvFileFormat = CSVFormat.DEFAULT.withHeader(FILE_HEADER_MAPPING);
//initialize CSVParser object
CSVParser csvFileParser = new CSVParser(fileReader, csvFileFormat);
//Get a list of CSV file records
List<CSVRecord> csvRecords = csvFileParser.getRecords();
// process accordingly
我的问题是,当我将要处理的 CSV 复制到我的目标目录和 运行 我的解析程序时,出现以下错误:
Exception in thread "main" java.lang.IllegalArgumentException: Index for header 'Title' is 7 but CSVRecord only has 6 values!
at org.apache.commons.csv.CSVRecord.get(CSVRecord.java:110)
at launcher.QualysImport.createQualysRecords(Unknown Source)
at launcher.QualysImport.importQualysRecords(Unknown Source)
at launcher.Main.main(Unknown Source)
但是,如果我将文件复制到我的目标目录,打开并保存它,然后再次尝试该程序,它就可以运行。打开并保存 CSV 文件会在末尾添加所需的逗号,这样我的程序就不会抱怨没有足够的 headers 来阅读。
对于上下文,这是 before/after 保存的示例行:
之前(失败):"data"、"data"、"data"、"data"
(工作后):"data","data","data","data",
所以我的问题是:为什么 CSV 格式在我打开和保存时会发生变化?我没有更改任何值或编码,并且 MS-DOS 或常规 .csv 格式在保存时的行为相同。另外,我在测试中使用 Excel 到 copy/open/save。
我需要使用一些编码或格式设置吗?我可以通过编程方式解决这个问题吗?
提前致谢!
编辑#1:
对于其他上下文,当我第一次查看原始文件中的空行时,它只有换行符 ^M,如下所示:
^M
在 Excel 中打开并保存后,我的所有 37 个空白字段看起来像这样:
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,^M
这是 Windows 编码差异吗?
也许试试这个: 为给定的文件创建一个解析器。 parse(File 文件, Charset 字符集, CSVFormat 格式)
//导入导入java.nio.charset.StandardCharsets; //StandardCharsets.UTF_8
注意:此方法使用 FileReader.FileReader(java.io.File) 在内部创建一个 FileReader,它又依赖于执行代码的 JVM 的默认编码。
或者尝试使用 AllowMissingColumnNames?
//intialize CSVFormat object
CSVFormat csvFileFormat = CSVFormat.DEFAULT.withHeader(FILE_HEADER_MAPPING).withAllowMissingColumnNames();
也许这是与最初生成文件的任何内容的兼容性问题。似乎 Excel 接受空行作为每列中包含空字符串的有效行,列数与其他行相匹配。然后它根据带有列分隔符的 CSV 约定保存它。 (^M 是 Carriage Return 字符;在 Microsoft 系统中,它位于文本文件行尾的 Line Feed 字符之前)
也许您可以通过创建自己的 Reader 子类来处理它,使其位于 FileReader 和 CSVParser 之间。您的 reader 将读取一行,如果它是空白的,则 return 一行包含正确数量的逗号。否则只是 return 该行原样。
例如:
class MyCSVCompatibilityReader extends BufferedReader
{
private final BufferedReader delegate;
public MyCSVCompatibilityReader(final FileReader fileReader)
{
this.delegate = new BufferedReader(fileReader);
}
@Override
public String readLine()
{
final String line = this.delegate.readLine();
if ("".equals(line.trim())
{ return ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"; }
else
{ return line; }
}
}
实现接口时还有很多其他细节需要正确实现。您需要传递对所有其他方法(关闭、就绪、重置、跳过等)的调用,并确保每个 read()
方法都能正常工作。如果文件很容易放入内存,那么只读取文件并将固定版本写入 CSVParser 的新 StringWriter then create a StringReader 可能会更容易。