在Java中解析.csv文件会返回outofbounds异常

时间:2015-02-18 17:49:50

标签: java csv indexoutofboundsexception

我有以下问题:我试图在java中解析.csv文件,并在2维数组中专门存储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)

然而,我意识到文件中的某些东西有一种奇怪的格式,例如例如,其中的一些文本在其中有新行,但没有任何方式涉及换行符。但是,如果我手动删除这些空白行,则生成的输出(在提示错误消息之前)会将数据添加到数组中,直到下一个空白行... 有谁知道如何解决这个问题?任何帮助都会受到高度赞赏......

4 个答案:

答案 0 :(得分:2)

您的第一个问题是您的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 \ n。 Windows使用\ r \ n来表示新行,而大多数其他系统只使用\ n。如果多个人/程序编辑了你的文本文件,那么完全有可能自己最终得到stray \ r \ n,大多数解析器都不容易处理它们。

在分割线条之前轻松检查问题是否存在的方法,执行

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

如果这是您重复多次的过程,则可能需要考虑使用扫描程序(或库)来获得更有效的文本处理。否则,你可以做到这一点。

答案 1 :(得分:0)

当您在CSV文件中有新行时,请在此行之后  while((line = buffer.readLine())!= null){ 变量行不会有CSV行,而只有一些没有的文本;

例如,如果您有文件

column1;column2;column
3 value

在第一次迭代后变量行将有

列1;列2;柱

在第二次迭代之后它会有 3值

当你调用“3 value”.split(“;”,6)时,它将返回带有一个元素的数组。然后当你调用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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

字符串的split(pattern,limit)方法返回一个数组,该数组的大小与找到的标记数一样,最多可达limit参数指定的数字。限制是最大值,而不是返回的最小数组元素数。

&#34; 1,2,3&#34;与(&#34;,&#34;,6)分开,返回3个元素的数组:&#34; 1&#34;,&#34; 2&#34;和&#34; 3&#34;。

&#34; 1,2,3,4,5,6,7&#34;将返回6个元素:&#34; 1&#34;,&#34; 2&#34;,&#34; 3&#34;,&#34; 4&#34;,&#34; 5&#34;和&#34;&#34; 6,7&#34;最后一个元素是愚蠢的,因为split方法在5之后停止了拆分,并将其余的源字符串作为第六个元素返回。

空行表示为空字符串(&#34;&#34;)。拆分&#34;&#34;将返回1个元素的数组,即空字符串。

在您的情况下,此处创建的字符串数组

String[] b = new String[6];

并将分配给b替换为

返回的数组
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...
        ...
    }

您可能还想考虑for循环。我不认为它对你有益。

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

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

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

将首次读取文件中的每一行。然后它将有9999次尝试读取文件,找不到任何新内容,并退出while循环。

因为while循环因为while循环将读取第1000个元素并且如果文件中有超过10000行而超出数组,则不会保护您不会读取超过10000个元素。考虑用arraylist或vector替换大数组,因为它们的大小将适合您的文件。