保存之前/之后由于CSV差异导致的错误解析(Java w/Apache Commons CSV)

cor*_*ria 5 java csv encoding apache-commons-csv

我有一个37列CSV文件,我使用Apache Commons CSV 1.2在Java中解析.我的设置代码如下:

//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
Run Code Online (Sandbox Code Playgroud)

我的问题是,当我将要处理的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)
Run Code Online (Sandbox Code Playgroud)

但是,如果我将文件复制到我的目标目录,打开并保存它,然后再次尝试该程序,它的工作原理.打开并保存CSV会添加最后所需的逗号,这样我的程序就无法获得没有足够的标题来阅读.

对于上下文,这里是保存之前/之后的示例行:

之前(失败):"数据","数据","数据","数据"

之后(工作):"数据","数据",,,"数据",,,"数据",,,,,,

所以我的问题是:为什么我打开并保存CSV格式会发生变化?我没有更改任何值或编码,保存时MS-DOS或常规.csv格式的行为是相同的.另外,我在我的测试中使用Excel来复制/打开/保存.

我需要使用一些编码或格式设置吗?我能以编程方式解决此问题吗?

提前致谢!

编辑#1:

对于其他上下文,当我第一次在原始文件中查看空行时,它只有新行^ M字符,如下所示:

^M
Run Code Online (Sandbox Code Playgroud)

在Excel中打开并保存之后,我的所有37个空字段看起来都像这样:

,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,^M
Run Code Online (Sandbox Code Playgroud)

这是Windows编码差异吗?

dsh*_*dsh 2

也许这与最初生成该文件的内容存在兼容性问题。Excel 似乎接受空行作为有效行,每列中包含空字符串,列数与其他行相匹配。然后它根据 CSV 约定并使用列分隔符保存它。(^M 是回车符;在 Microsoft 系统上,它位于文本文件中行尾的换行符之前)

也许您可以通过创建自己的Reader子类来位于 FileReader 和 CSVParser 之间来处理它。您的阅读器将读取一行,如果该行为空,则返回包含正确逗号数量的行。否则就按原样返回该行。

例如:

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; }
        }
    }
Run Code Online (Sandbox Code Playgroud)

实现接口时还有很多其他细节需要正确实现。您需要传递对所有其他方法(关闭、就绪、重置、跳过等)的调用,并确保每种read()方法都能正常工作。如果文件很容易适合内存,那么只需读取文件并将固定版本写入新的StringWriter,然后为 CSVParser创建StringReader可能会更容易。