Java:读取 .csv 文件并保存到数组中

3
我在尝试读取.csv文件并将每一列保存到数组时遇到异常问题。尽管程序看起来很长,但实际上只有15个不同的数组。
出现了这个异常:"Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2",它出现在以下代码行:

department[i] = dataArray[2];

请问我该怎么解决这个问题呢?
      BufferedReader CSVFile = 
            new BufferedReader(new FileReader("Sub-Companies.csv"));

      String dataRow = CSVFile.readLine();
      // Read the number of the lines in .csv file 
      // i = row of the .csv file
      int i = 0; 
      while (dataRow != null){
          i++;
          dataRow = CSVFile.readLine();

        }
      System.out.println(i);
      // Close the file once all data has been read.
      CSVFile.close();

      // End the printout with a blank line.
      System.out.println();

      // Save into arrays
      customer_id = new String[i];
      company_name = new String[i];
      department = new String[i];
      employer = new String[i];
      country = new String[i];
      zipcode = new String[i];
      address = new String[i];
      city = new String[i];
      smth1 = new String[i];
      smth2 = new String[i];
      phone_no1 = new String[i];
      phone_no2 = new String[i];
      email = new String[i];
      website = new String[i];
      customer_no = new String[i];

      // Read first line.
      // The while checks to see if the data is null. If 
      // it is, we've hit the end of the file. If not, 
      // process the data.
      int j;
      int counter;
      i = 0;

      // Read the file again to save the data into arrays
      BufferedReader CSV = 
            new BufferedReader(new FileReader("Sub-Companies.csv"));

      String data = CSV.readLine();

      while (data != null){
          String[] dataArray = data.split(";");
          for (String item:dataArray) {
            customer_id[i] = dataArray[0];
            company_name[i] = dataArray[1];
            department[i] = dataArray[2];
            employer[i] = dataArray[3];
            country[i] = dataArray[4];
            zipcode[i] = dataArray[5];
            address[i] = dataArray[6];
            city[i] = dataArray[7];
            smth1[i] = dataArray[8];
            smth2[i] = dataArray[9];
            phone_no1[i] = dataArray[10];
            phone_no2[i] = dataArray[11];
            email[i] = dataArray[12];
            website[i] = dataArray[13];
            customer_no[i] = dataArray[14];
            }


          //System.out.print(address[i] + "\n"); 
          data = CSV.readLine(); // Read next line of data.
          i++;
      }

提前致谢!

一些数据是"E3B3C5EB-B101-4C43-8E0C-ADFE76FC87FE;"Var Welk" Inh. Kar;NULL;NULL;DE;16278;Rotr 3;Angermünde;NULL;NULL;03331/354348-0;0343331/364548-15;info@aalls.com;http://www.adss.com;ipo241",但也可能有所不同(更小或更大)。


4
何必重复造轮子?使用像Super CSV或我最喜欢的opencsv这样的CSV解析库即可。 - dnault
1
该错误被抛出以指示使用非法索引访问了数组。即索引大于或等于数组大小。 - Emmanuel N
1
String[] dataArray = data.split(";"); 之后执行 System.out.println("array elements: " + dataArray.length); - mcalex
@DimitraMicha 好的,只是为了调试目的,请注释掉 //department[i] = dataArray[2]; 然后看看你是否能够访问其他索引(我相信你不会能够访问)。 - PermGenError
这是因为该值为空吗? - mcalex
显示剩余11条评论
7个回答

5
这应该能解决问题:它基本上创建了csv文件的矩阵表示形式。
LinkedList<String[]> rows = new LinkedList<String[]>();
String dataRow = CSVFile.readLine();
// Read the number of the lines in .csv file 
// i = row of the .csv file
int i = 0; 
while ((datarow = CSVFile.readLine()) != null){
    i++;
    rows.addLast(dataRow.split(","));
}

String[][] csvMatrix = rows.toArray(new String[rows.size()][]);

在csvMatrix[row][col]中...
当访问一个列时,需要进行断言以确保你要访问的列号在范围内,方法如下:
if(col < csvMatrix[row].length)

我明白你的意思,但我不明白 if(col < csvMatrix[row].length) 是如何工作的。 - Dimitra Micha
抱歉,我在这一行中漏掉了一些信息: 并不是因为一行有X列,所有的行都会有相同的数量... 所以为了谨慎起见,在尝试访问索引之前,请验证索引是否存在:在你的情况下,该行可能是无效的,因此你可能希望忽略将其添加到列表中...所以在循环中,你可以将"rows.addLast(dataRow.split(","));"替换为以下内容: String[] temp = dataRow.split(","); if(temp.length == ACCEPTED_COLUMN_COUNT) { rows.addLast(temp); } - doctor killer
嘿,@doctor killer。我刚刚解决了我的问题。非常感谢您的回复。 - Dimitra Micha

2

你的代码存在几个问题。异常是由于其中一行缺少分号分隔的值导致的。

你的代码中有一个奇怪的部分:

  for (String item:dataArray) {
    customer_id[i] = dataArray[0];

这意味着您需要重复相同的任务15次(只需删除for(String item:...)即可)。
如果我是您,我会执行以下操作:
创建一个类,类似于这样:
public class Customer {
    private String customerId;
    private String companyName;

    // ...
    public static Customer create(final String... args) {
        if (args.length != 15) {
            return null; // or throw an exception
        }
        final Customer rv = new Customer();
        rv.setCustomerId(args[0]);
        rv.setCompanyName(args[1]);
        // ...
        return rv;
    }

    public String getCustomerId() {
        return customerId;
    }

    public void setCustomerId(final String customerId) {
        this.customerId = customerId;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(final String companyName) {
        this.companyName = companyName;
    }
}

使用集合(如上面的帖子建议):
    BufferedReader csv = new BufferedReader(new FileReader("Sub-Companies.csv"));
    List<Customer> customers = new LinkedList<Customer>();

    String data;
    while ((data = csv.readLine()) != null){
        Customer customer = Customer.create(data.split(";"));
        if (customer != null) {
            customers.add(customer);
        }
    }

如果你需要数组而不是集合,可以这样做:

如果您需要的是数组而不是集合,则可以执行以下操作:

Customer[] arr = customers.toArray(new Customer[customers.size()]);

使用库来读取文件...例如,您可以尝试使用http://opencsv.sourceforge.net/

我在考虑使用 if (args.length != 15) { return null; // or throw an exception 这个条件,然后将每个数组保存为array[15],其他条目为NULL。 - Dimitra Micha

2

最好使用 ArraList<String>,如果你想 转换为数组

你的问题是你在计算行数以创建数组大小,但是你基于分号拆分(";")添加数据,因此数组长度和可用于从拆分(";")中添加到数组中的值不匹配。


例如,您有两行代码,当您要创建数组时,使用行计数为2作为数组大小。但是,当您使用分隔符“;”进行拆分时,其大小为4,并且您尝试向只能容纳2个值的数组添加4个值。因此,将出现java.lang.ArrayIndexOutOfBoundsException异常。就是这样。 - sunleo
现在我有点困惑了,在每个数组中,我保存了每一行的一个条目。所以这些条目等于文件的行数,对吗? - Dimitra Micha
@mcalex 在你提出的打印之后,是的,它们都是15:( - Dimitra Micha
看起来你有2行数据,你创建了大小为2的数组。但是通过分割单个行可以得到4个或更多的值。当你尝试将4个值添加到只能容纳2个值的数组中时,会发生异常。 - sunleo
@DimitraMicha请打印出customer_id.length和代码结尾处的i值。这样你就能摆脱这个问题了。并让我们知道结果。 - sunleo
显示剩余6条评论

1
department[i] = dataArray[2];  

异常表示dataArray没有那么多元素(即3个)。
如果您想解析CSV文件,可以通过指定任何缺失元素必须有占位符来简化您的生活。
我的意思是,您可以拥有一个记录:a;b;c;d;e;f;g;h;j
其中每个字符代表您的列的值,但当一个元素缺失时,格式必须为:a;;;;;f;g;h;j而不是a;f;g;h;j

这不是CSV文件中不寻常的期望,而是规范,可以大大简化您的代码,并避免数组索引异常,因为您的行将始终具有预期的列。


如果你想解析你的CSV文件,你可以指定对于任何缺失的元素都必须有占位符,这样会让你的生活更轻松。我理解你的意思,不过有没有什么方法能够实现呢?也许我可以读取分号出现的次数,然后再添加分号来补全。 - Dimitra Micha
但是你的CSV文件是如何生成的呢?如果是手动生成的,那么只需为每个缺失的元素添加多余的;即可。如果是通过代码生成的,则同样如此。因此,当您解析CSV行时,执行split(';')操作时,您就知道返回数组的大小。 - Cratylus
这个 CVS 文件是给我的。是的,当我分割它时我知道它。 - Dimitra Micha
如果给定了文件,您应该明确 CSV 格式的期望。 - Cratylus

1
使用 ArrayList:
public ArrayList<ArrayList<String>> parseDataFromCsvFile()
{
     ArrayList<ArrayList<String>> dataFromFile=new ArrayList<ArrayList<String>>();
     try{
         Scanner scanner=new Scanner(new FileReader("CSV_FILE_PATH"));
         scanner.useDelimiter(";");

         while(scanner.hasNext())
         {
            String dataInRow=scanner.nextLine();
            String []dataInRowArray=dataInRow.split(";");
            ArrayList<String> rowDataFromFile=new ArrayList<String>(Arrays.asList(dataInRowArray));
            dataFromFile.add(rowDataFromFile);
         }
         scanner.close();
     }catch (FileNotFoundException e){
        e.printStackTrace();
     }
     return dataFromFile;
}

调用方法(显示csv内容):
ArrayList<ArrayList<String>> csvFileData=parseDataFromCsvFile();

public void printCsvFileContent(ArrayList<ArrayList<String>> csvFileData)
{
    for(ArrayList<String> rowInFile:csvFileData)
    {
        System.out.println(rowInFile);
    }
}

0
如果你想使用Gradle(而不是Maven)将数据加载到Parameterized JUnit测试中,这里是方法:
// import au.com.bytecode.opencsv.CSVReader;
@Parameters(name = "{0}: {1}: {2}")
public static Iterable<String[]> loadTestsFromFile2() {
    String separator = System.getProperty("file.separator");
    File tFile = loadGradleResource( System.getProperty("user.dir") + 
        separator +  "build" + separator + "resources" + separator +  "test" + 
            separator + "testdata2.csv" );
    List<String[]> rows = null;
    if ( tFile.exists() ) {
        CSVReader reader = null;
        try {
            reader = new CSVReader( new FileReader( tFile ), ',' );
            rows = reader.readAll();
        } catch (FileNotFoundException e) {
                e.printStackTrace();
        } catch (IOException e) {
                e.printStackTrace();
        }   
    }
    staticlogger.info("Finished loadTestsFromFile2()");
    return rows;
} 

0
请检查是否可以使用 java.util.StringTokenizer
示例:
StringTokenizer tokenizer = new StringTokenizer(inputString, ";")

手册:StringTokenizer文档


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接