【问题标题】:java: reading all csv files at once and sum over elements/rowsjava:一次读取所有csv文件并对元素/行求和
【发布时间】:2017-02-09 07:23:08
【问题描述】:

我有这个函数,它假设打开一个文件并读取第一个元素[第一行][第一列],将它存储在一个 3D 数组中[文件索引][行][列],然后打开下一个文件并执行相同的过程。此操作应一直运行,直到所有文件中的所有行和列都存储在 3D 数组中。 我的问题是,这个函数完成了这项工作,但它只读取每个文件的第一行(行)并停在那里!! 任何专家都可以帮助我弄清楚我的功能发生了什么吗?我已经使用 java 大约两个月了:/

public static double[][][] fetch_data(int start, int end, int column) throws IOException{
    // start and end are for files start and end index. 
    int length = file_length(start); // since all files have the same length. The file_length() function just determine the length.
    data = new double [number_of_files][length][number_of_columns];
    int sum = 0;
    for (int i = 0; i < length; i++){
        for (int index = start; index < end; index++){
            Scanner scan = new Scanner (new BufferedReader(new FileReader(file_call(index)))); // file_call() is a function that lookup the file dir.
            scan.nextLine();
                String scanedData = scan.nextLine();
                String [] array = scanedData.split(",");
                data[index][i][colum] = Double.parseDouble(array[index]);
                sum += data[index][i][colum];
                System.out.println(" element " + data[index][i][column]); // just to test the loop
            }
        }
        return data;
    }

数据文件如下所示: 文件(1):

A, B, C, D 
1, 2, 3, 4
13, 2, 3, 4
1, 2, 3, 4
...

文件(2):

A, B, C, D 
5, 6, 7, 8
55, 6, 7, 8
5, 6, 7, 8
...

文件(3):

A, B, C, D 
9, 10, 11, 12
9, 10, 11, 12
91, 10, 11, 12
...

预期的输出(我们忽略了上面代码中的第一行“A,B C,D”):

1+5+9,  2+6+10, ...
13+55+9, 2+6+10, ...
1+5+91,  2+6+10,  ...
...

我希望找到一个java专家,他可以教我如何让我for循环遍历每个文件的所有行,例如读取所有文件中的第一行并再次返回并读取所有文件中的第二行等等。

非常感谢你们的帮助。

最好的

【问题讨论】:

  • 我建议你使用调试器来单步调试你的代码,看看它在做什么。
  • 您还应该关闭计算机并准备一张纸和铅笔。用文字写出解决问题的步骤。
  • 谢谢大家,我做了调试,结果发现它只读取第一行并停止。
  • @Code-Apprentice,我有,我的房间里实际上有一块大白板,我在上面做所有的事情。
  • 我建议你阅读一下if 的作用。

标签: java csv sum


【解决方案1】:

这不是loop:

if (scan.hasNext()){

由于您每次都在重新创建扫描程序,因此您每次都是从文件顶部重新开始。你有几个选择。要么:

  1. (低效)为每个文件的每次迭代创建一个新的 Scanner,并跳到正确的行
  2. 维护一个扫描器列表并一起读取所有文件
  3. 维护一个文件内容列表并顺序读取每个文件

如果您选择选项 3,则需要在之后使用另一个循环处理结果。

【讨论】:

  • 谢谢,我实际上发现这个“if”条件在我的代码中没有用,我会删除它。但是正如你所看到的,上面有两个主循环,第三个是在函数之外实现的。还是你的意思是别的?!
  • @AhmedAbdelrahman 如果您在我的帖子中单击 loop 一词,它将带您进入有关 java while 循环的教程。然后你应该考虑它在你检查 hasNext() 的这个位置是如何有用的
  • 再次感谢帕特里克,“while”条件的问题是它不会停止,直到它一次读取一个文件中的所有行然后移动到下一个文件。虽然我希望输出如图所示,但每个文件的行。我为此苦苦挣扎,直到我切换到“for”循环,这似乎可以完成这项工作,但有一点问题。
  • 谢谢@Patrick。看起来我在上面的代码中遵循方法(1)。选项 (3) 似乎在计算上相当昂贵,因为我正在处理大型数据文件。选项 (2) 是合理的,但您能告诉我如何维护扫描仪列表吗?你有这方面的教程吗?再次感谢。
  • @AhmedAbdelrahman 在您的代码中,您已经构建了所需大小的 double[][][]。那你怎么能说它是太多的数据呢?此外,rustot 已经为您提供了填充 double[][][] 的代码。剩下的就是让你迭代它。
【解决方案2】:
private static double[][] getCsv(Path path) {
  try {
    return Files.lines(path).map(line -> 
      Arrays.stream(line.split(",")).mapToDouble(Double::valueOf).toArray()
    ).toArray(double[][]::new);
  } catch (IOException e) { 
    throw new UncheckedIOException(e); 
  }
}

...

Path[] paths = ...

double[][][] result = Arrays.stream(paths).map(
    path -> getCsv(path)
).toArray(double[][][]::new);

【讨论】:

  • 谢谢@rustot。这段代码有点超出我的水平,但我花了一些时间来弄清楚。在“getCsv”方法中,您以某种方式将字符串行映射为通过“Files.lines(path).map(...)”加倍,然后将其转换为 2D 数组,稍后将通过“result”转换为 3D 数组.首先,我尝试了这段代码,但似乎“getCsv”方法有问题,我真的不知道!我也看不到这将如何从每个文件中取行并重复!除非我遍历文件的索引!!不过,感谢您的帮助。
  • stream -> do something -> toArray() 在这种情况下,只允许在进程结束时分配数组,此时实际大小已知。在您的示例中有 3 个嵌套流,例如 3 个嵌套循环。第一个文件名流,文件中的嵌套行流和行中的嵌套数字流。每个流最终折叠成数组
【解决方案3】:

我可以在这里看到一些错误的东西:

  1. 在data[index][i][colum] = Double.parseDouble(array[index]); 行中,变量colum 未定义(拼写错误?)
  2. 在同一行中,array[index] 可能不正确,因为您必须从 array 读取所有值,而不仅仅是单个值。此外,index 是文件索引,而不是array 内的索引。您应该遍历array,其中column 是列索引。即:

    for (int column = 0; column < array.length; column++) {
        data[index][i][column] = Double.parseDouble(array[column]);
    }
    

【讨论】:

    【解决方案4】:

    好的,我设法通过编写以下函数解决了我的小问题。 下面的函数,去读取每个文件中的第一列,并将其视为为 x 轴存储的值。在这个函数中,我们不会遍历文件索引,因为所有文件的第一列都是相同的,所以从任何文件中获取第一列就足够了。如果您的文件不同,则只需遍历文件索引,如下面的第二个函数解释 y 轴。

    //================================= Generate x-data for x-axis
        public static double[][] get_x_data(int start, int end, int node) throws IOException{
            // start: for start file, end: for end file, node: for column
            // storing the volt column from the first file, we call it base experiment
            // base file is specified by the user or the machine will take the first file as base by default
            int base_index = 0; // should be implemented in the gui later
    
            if (base_index == 0){
                index = 1;
                @SuppressWarnings("resource")
                int length =  data_vault.file_length(index); // "file_length" is a function that determine the length of the file.
                String filename = data_vault.file_call(index); // "file_call" is a function that seek the name of the file from some directory. But you can insert you file directly here!
                data_x = new double [length][end];
                Scanner scan = new Scanner (new BufferedReader(new FileReader(filename)));
                scan.nextLine();
                for (int i = 0; i < length; i++){
                    String scanedData = scan.nextLine();
                    String [] array = scanedData.split(",");
                    data_x[i][index] = Double.parseDouble(array[0]);
                    x_value = data_x[i][index]; // no need for this
                    //System.out.println("x-axis " + data_x[i][index] + " file index " + index + " read " + i);
                }
            }
            return data_x;
        }
    

    下面的函数独立查找每一列并将其视为 y 轴值:它有一个内部循环,贯穿每个文件的行(我从零运行到文件的最后一行,长度)。这个“i”循环被另一个循环包裹,该循环在文件索引(file_1、file_2、...、file_n)上运行。然后所有函数都被一个在列上运行的循环包裹。下面是生成 y 轴实体的函数:

    //================================= Generate y-data for y-axis
    public static double[][] get_y_data(int start, int end, int node) throws IOException{
    
        for (int index = start; index < end; index++){
            @SuppressWarnings("resource")
            int length =  data_vault.file_length(index);
            data_y = new double [length][end];
            String filename = data_vault.file_call(index);
            Scanner scan = new Scanner (new BufferedReader(new FileReader(filename)));
            scan.nextLine();
            for (int i = 0; i < length; i++){
                String scanedData = scan.nextLine();
                String [] array = scanedData.split(",");
                data_y[i][index] = Double.parseDouble(array[node]);
                y_value = data_y[i][index]; // no need for this
                //System.out.println(" y-axis " + y_value);
            }
        }
        return data_y;
    }
    

    注意这里的'index'允许用户选择从哪里开始读取他的文件,从第一个或'n'文件开始。索引循环将从'int start' 开始并循环到'int end'。

    然后你基本上可以用这些矩阵做任何你想做的事情。我希望这个解决方案对某人有所帮助。

    最适合你们。

    【讨论】:

      【解决方案5】:

      如果我理解正确,您的代码中有一些不必要的部分。我会这样做:

      • 初始化一个四列十行的双精度数组(我不知道大小是否应该固定——现在我们假设它是固定的——你会更容易地扩展它以使其更通用):

        double[][] m = new double[10][4];

      这是一个零数组。

      • 现在,假设n 是您要读取的文件数,因此您在for 循环中读取(将执行n 次)所有文件,一个接一个。在for 循环中,您使用while 循环和hasNext() 方法作为条件。在while 循环中,您读取另一行,将其拆分为数字,然后将相应的数字添加到一行中的每个单元格(您将需要额外的迭代器来指定下一行)。

      独立行的大部分操作都是在您发布的代码中完成的,因此对您来说应该不是问题。

      这只是一个想法。

      第二种方法

      我可以想到一种没有while 循环的方法。考虑使用Scanner 类,使用useDelimeter() 方法,您会将文件拆分为单独的行,然后将单独的行拆分为数字。您需要数组n x r x c,其中n 是文件数,r 行数,c 列数。在那里,您将存储文件中的数据。有了这个三维数组,您可以轻松地以任何您想要的方式添加值。

      【讨论】:

      • 哦,伙计@Michal,你把我的噩梦带回来了,“while”循环!我使用“for”循环的主要原因是因为“while”循环获取整个文件,直到它到达文件中的最后一行然后移动到下一个文件才停止,这是我真正不这样做的不想要。在您的评论“现在,假设 n 是数字......”这是读取任何数据文件的常用方法,我试图通过使用“for”循环来覆盖这种方法。
      • 为什么不能在while 循环中读取整个文件?我不明白你为什么不这样做?特别是,当文件的行数不同时,你不能从所有文件中读取一行,然后从所有文件中读取第二行,等等。好吧,也许你可以,但可能会导致问题,这是你在使用时不会遇到的问题while 循环。
      • 当然可以,但没有while循环。这就是我切换到 for 循环的平均原因。我设法解决了我的问题,我很快就会发布我的解决方案来帮助可能需要它的其他人。
      猜你喜欢
      • 2021-06-02
      • 2013-04-20
      • 1970-01-01
      • 1970-01-01
      • 2013-08-04
      • 1970-01-01
      • 2011-09-18
      • 1970-01-01
      相关资源
      最近更新 更多