解析 .csv 文件中 java returns 越界异常

Parse .csv File in java returns outofbounds exception

我遇到以下问题:我正在尝试解析 java 中的 .csv 文件,并将其中的 3 列具体存储在二维数组中。该方法的代码如下所示:

    public static void parseFile(String filename) throws IOException{
    FileReader readFile = new FileReader(filename); 
    BufferedReader buffer = new BufferedReader(readFile);
    String line; 
    String[][] result = new String[10000][3];
    String[] b = new String[6];

    for(int i = 0; i<10000; i++){
            while((line = buffer.readLine()) != null){
                b = line.split(";",6);
                System.out.println("ID: "+b[0]+" Title: "+b[3]+ "Description: "+b[4]); // Here is where the outofbounds exception occurs...


                result[i][0] = b[0];
                result[i][1] = b[3];    
                result[i][2] = b[4];
                }
            }
            buffer.close();

}

我觉得我必须说明这一点:.csv 文件很大。它有 32 列,和(几乎)10.000 个条目(!)。 解析时,我不断收到以下信息:

    XXXXX CHUNKS OF SUCCESFULLY EXTRACTED CODE
    Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException:3
    at ParseCSV.parseFile(ParseCSV.java:24)
    at ParseCSV.main(ParseCSV.java:41)

但是,我意识到文件中的某些内容具有奇怪的格式,例如例如,其中的一些文本中有换行符,但没有以任何方式涉及换行符。但是,如果我手动删除那些空行,生成的输出(在提示错误消息之前)会将内容添加到数组中,直到下一个空行...... 有谁知道如何解决这个问题?任何帮助将不胜感激...

当您的 CSV 文件中有新行时,在该行之后 while((line = buffer.readLine()) != null){ 可变行将没有 CSV 行,只有一些没有 ;

的文本

例如,如果您有文件

column1;column2;column
3 value

第一次迭代后变量行将有

第 1 列;第 2 列;第

第二次迭代后它将有 3 值

当你调用 "3 value".split(";",6) 时,它将 return 包含一个元素的数组。稍后当你调用 b[3] 时它会抛出异常。

CSV格式有很多小东西,要实现这些你会花很多时间。这是一篇关于所有可能的 csv 示例的好文章 http://en.wikipedia.org/wiki/Comma-separated_values#Basic_rules_and_examples

我会向您推荐一些像这样的现成的 CSV 解析器

https://commons.apache.org/proper/commons-csv/apidocs/org/apache/commons/csv/CSVParser.html

您的第一个问题是您的 csv 文件中可能至少有一个空行。您需要更换:

b = line.split(";", 6);

b = line.split(";");
if(b.length() < 5){
   System.err.println("Warning, line has only " + b.length() + 
                      "entries, so skipping it:\n" + line);
   continue;
} 

如果您的输入可以合法地在您的条目中包含新行或嵌入分号,这是一个更复杂的解析问题,您最好使用第三方解析库,因为有几个非常好的一个。

如果您的输入不应该有新行,问题可能是 \r。 Windows 使用 \r\n 表示换行,而大多数其他系统只使用 \n。如果多个 people/programs 编辑了您的文本文件,则完全有可能自己以杂散的 \r 结束,大多数解析器都不容易处理。

在拆分线路之前,可以轻松检查这是否是您的问题,请执行

line = line.replace("\r","").

如果这是一个您要重复多次的过程,您可能需要考虑使用扫描仪(或库)来获得更高效的文本处理。否则,你可以凑合一下。

访问b[]前请检查b.length>0

String 的 split(pattern, limit) 方法 returns 一个数组,其大小为找到的标记数量,最多为 limit 参数指定的数量。 Limit 是数组元素的最大值,而不是最小值 returned.

"1,2,3" 与 (",", 6) 拆分,return 包含 3 个元素的数组:"1"、"2" 和 "3"。

"1,2,3,4,5,6,7" 将 return 6 个元素:"1"、"2"、"3"、"4"、"5" 和 " "6,7" 最后一个元素是愚蠢的,因为 split 方法在 5 之后停止拆分并且 return 将源字符串的其余部分作为第六个元素。

空行表示为空字符串 ("")。拆分 "" 将 return 一个包含 1 个元素的数组,即空字符串。

在你的例子中,这里创建的字符串数组

String[] b = new String[6];

并分配给 b 被 return 数组替换为

b = line.split(";",6);

并在看不见和不受欢迎的垃圾收集器手中遇到了它的最终命运。

更糟糕的是,在空行的情况下,它被替换为一个元素数组,所以

System.out.println("ID: "+b[0]+" Title: "+b[3]+ "Description: "+b[4]);

尝试访问 b[3] 时崩溃。

建议的解决方案是

while((line = buffer.readLine()) != null){
    if (line.length() != 0)
    {
            b = line.split(";",6);
            System.out.println("ID: "+b[0]+" Title: "+b[3]+ "Description: "+b[4]); // Here is where the outofbounds exception occurs...
        ...
    }

或(更好,因为前一个可能会绊倒格式错误的线路)

while((line = buffer.readLine()) != null){
    b = line.split(";",6);
    if (b.length() == 6)
    {
            System.out.println("ID: "+b[0]+" Title: "+b[3]+ "Description: "+b[4]); // Here is where the outofbounds exception occurs...
        ...
    }

您可能还想考虑 while 周围的 for 循环。我不认为这对你有任何好处。

 while((line = buffer.readLine()) != null)

将读取文件中的每一行,所以

for(int i = 0; i<10000; i++){
        while((line = buffer.readLine()) != null){

将第一次读取文件中的每一行。然后它将有 9999 次尝试读取文件,没有发现任何新内容,然后退出 while 循环。

由于 while 循环,您无法避免读取超过 10000 个元素,因为如果文件中的行超过 10000 行,while 循环将读取第 10001 个元素并超出您的数组。考虑用 arraylist 或 vector 替换大数组,因为它们的大小将适合您的文件。