【问题标题】:Parse .csv File in java returns outofbounds exception在 java 中解析 .csv 文件返回 outofbounds 异常
【发布时间】:2015-02-18 17:49:50
【问题描述】:

我有以下问题:我正在尝试在 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)

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

【问题讨论】:

    标签: java csv indexoutofboundsexception


    【解决方案1】:

    您的第一个问题是您的 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。如果多个人/程序编辑了您的文本文件,则完全有可能自己最终得到杂散的 \r ,大多数解析器都不容易处理。

    一种轻松检查是否是您的问题的方法是在您拆分线路之前,这样做

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

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

    【讨论】:

    • CSV 中的新行是可能的。例如,这些示例是正确的 CSV(从这里复制 en.wikipedia.org/wiki/…1997,Ford,E350,"Go get one now they are going fast" 1997,Ford,E350,"Super, luxurious truck" 还想一想,如果你有,你会做什么;或 " 作为列值。
    • 这是一个很好的观点,我假设他可以控制他的输入,因为他提到输入中没有嵌入换行符,但是作为通用解决方案,应该注意它只当您没有任何额外的换行符或分号时有效....
    • 感谢您的反馈。您的解决方案有效,输出生成整个 .csv 文件并在运行时成功提示 err mess 到控制台!
    • ... 我需要在 split() 中使用什么类型的正则表达式才能忽略 ;位于字符串内部,同时保持拆分在 ; .csv 文件的单元格之间...?
    • 不幸的是,一旦你必须处理转义规则,你就会遇到上下文相关的解析问题,而这些问题不能用 reg-exes 很好地处理。如果必须处理它们,最好的办法是使用 CSV 解析器,例如下面提到的 Commons 解析器。
    【解决方案2】:

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

    的文本

    例如,如果你有文件

    column1;column2;column
    3 value
    

    第一次迭代后变量行会有

    column1;column2;column

    在第二次迭代后,它将有 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

    【讨论】:

      【解决方案3】:

      String 的 split(pattern, limit) 方法返回一个数组,该数组的大小为找到的令牌数,不超过 limit 参数指定的数目。 limit 是返回的数组元素的最大值,而不是最小值。

      "1,2,3" 用 (",", 6) 分割,返回一个包含 3 个元素的数组:“1”、“2”和“3”。

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

      空行表示为空字符串 ("")。拆分 "" 将返回一个包含 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...
              ...
          }
      

      您可能还想考虑 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 个元素并溢出您的数组。考虑用数组列表或向量替换大数组,因为它们的大小会适合您的文件。

      【讨论】:

        【解决方案4】:

        在访问b[]之前,请检查b.length&gt;0

        【讨论】:

        • 你确定只要检查数组大小是否大于0就可以解决。他得到java.lang.ArrayIndexOutOfBoundsException:3。你可能会重新考虑你的答案
        猜你喜欢
        • 1970-01-01
        • 2014-05-20
        • 1970-01-01
        • 2014-03-30
        • 2013-05-11
        • 2013-04-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多