【问题标题】:How To Read Line Numbers x Through (x+y) From A Very Large File如何从一个非常大的文件中读取行号 x 到 (x+y)
【发布时间】:2017-12-12 01:07:30
【问题描述】:

我有一个非常大的文本文件,其中每一行都必须被解析。 我想通过 x+100000 读取行 x 并将每一行添加到 List 中,使得 list.size

下面的代码是一个使用 BufferedReader 的版本。我很好地返回了前 100k 行,但在下一次调用中,从第 100k+1 行开始,它在第 150k 行左右开始减速,在第 165k 行附近它导致 OutOfMemoryError。我四处寻找一种方法来清空缓冲区,一旦它到达我想要开始添加到列表的行,但我找不到任何信息。我还试图找到一种方法来跳过 x 行,但我找不到任何东西。

public List<MyModel> retrieve(File inputFile, int startLine, String checksum) throws DaoException {

    List<MyModel> result = new ArrayList<>();
    try (BufferedReader br = new BufferedReader(new FileReader(inputFile))) {

          String line = null;
          int row = 0;
          int iteration = 0;

          try {
              while (((line = br.readLine()) != null) && iteration < MAX_ROWS) {
                  row++;
                  LOGGER.info("row: " + row + ", iteration: " + iteration);
                  if (row > startLine && iteration < MAX_ROWS) {
                      MyModel model = this.fileReader.populateMyModel(line);
                      model.setFileChecksum(checksum);
                      result.add(model);
                      iteration++;                    
                  }
                  if (iteration >= MAX_ROWS) {
                      break;
                  }
              }
          } catch (Exception e) {

              throw new FileReaderException("Failed to read line " + iteration + " of " + inputFile.getAbsolutePath(), e);
          }
    } catch (FileNotFoundException e1) {
          throw new FileReaderException("Could not find file '" + inputFile.getAbsolutePath() + "'.", e1);
    } catch (IOException e1) {
          throw new FileReaderException("Could not read file '" + inputFile.getAbsolutePath() + "'.", e1);
    }

        return result;
}

在尝试了解如何跳过行时,我遇到了 Java 8 Stream 读取文件的方式,下面的代码是我尝试以这种方式处理它。这在第一次调用时也很有效,返回前 100k 行。对于从第 100k+1 行开始的第二次调用,它返回“java.lang.IllegalStateException:流已被操作或关闭”。此外,我只想读取 x 到 x+100k 行然后返回,而不是循环浏览文件的所有行。我是这个 Stream 对象的新手,但似乎使用它应该提供解决方案。

public List<MyModel> retrieve(File inputFile, int startLine, String checksum) throws DaoException {
    List<MyModel> result = new ArrayList<>();

    try (Stream<String> lines = Files.lines(inputFile.toPath(), Charset.defaultCharset())) {
        lines.skip(startLine);
        lines
        .filter(line -> result.size() <= 100000)
        .forEach(line -> {
            result.add(this.fileReader.populateMyModel(line));
            if (result.size() % 10000 == 0) {
                LOGGER.info("result size: " + result.size());
            }
        });
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return result;
}

任何建议都会有所帮助。

【问题讨论】:

  • 你应该用skip().filter(...)...链接,跳过返回一个新的流,你不能重用一个消费的流。
  • @holi-java - 虽然我的问题文本中提到了该异常,但该链接并未解决如何从文件中读取一组特定的行号,这就是这个问题的意义所在.
  • 好吧,如果你认为它是合理的。我恢复它。确实,在将您的答案标记为重复之前,我是答案的第一个投票者。我只想告诉你那里有完整的详细答案。

标签: java java-8 stream java-stream


【解决方案1】:

当你写作时:

lines.skip(startLine)

您创建了一个新流,但没有保存对它的引用,因此您失去了操作。

我怀疑你想要类似的东西:

return lines.skip(startLine)
            .limit(100000)
            .map(fileReader::populateMyModel)
            .collect(toList());

【讨论】:

  • 我按照您的指导更改了代码,但仍然收到 OutOfMemoryError。即使我将行数减少到 10000result = lines.skip(startLine) .limit(10000) .map(fileReader::populateImportShippingNetRecord) .collect(Collectors.toList());
  • 我在同事的计算机上运行了相同的修复程序,它运行良好。它仍然放慢了速度,但它从未耗尽内存。我现在必须检查我的系统,但这似乎是解决方案。谢谢。
  • 读取 100000 行并存储它们不会占用太多内存(除非每行有 1000 个字符长)。问题可能是由于很长的行或您没有显示的 populateImportShippingNetRecord 方法。您可以使用分析器找出内存的使用位置。
  • 事实证明,这是一个循环内的数组列表的错误实例化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-11
  • 2011-02-18
相关资源
最近更新 更多