在Node.js中读取文本文件的第N到第M行

3

虽然我找到了很多逐行读取文本文件或读取第N行的示例,但我找不到任何关于如何从第N行读取到第M行的内容。

该文件相当大,约5 GB(约1000万行)。

编辑:这些行没有固定的长度。

1个回答

3

您可以使用readline功能,将文件作为流读取,而无需将其全部加载到RAM中。以下是如何实现的示例:

const fs = require('fs');
const readline = require('readline');

function readFromN2M(filename, n, m, func) {
  const lineReader = readline.createInterface({
    input: fs.createReadStream(filename),
  });

  let lineNumber = 0;

  lineReader.on('line', function(line) {
    lineNumber++;
    if (lineNumber >= n && lineNumber < m) {
      func(line, lineNumber);
    }
  });
}

让我们试一试:

// whatever you would like to do with those lines
const fnc = (line, number) => {
  // e.g. print them to console like this:
  console.log(`--- number: ${number}`);
  console.log(line);
};

// read from this very file, lines from 4 to 7 (excluding 7):
readFromN2M(__filename, 4, 7, fnc);

这将输出:

//  --- number: 4
//  function readFromN2M(filename, n, m, func) {
//  --- number: 5
//    const lineReader = readline.createInterface({
//  --- number: 6
//      input: fs.createReadStream(filename),

行号从1开始计数。要从0开始,请稍微修改编号。

更新:

我刚刚意识到,这种方法在某种程度上不是100%安全的。如果某个文件没有以新行字符结尾,则最后一行将无法以这种方式读取。这就是readline的设计方式......为了克服这一问题,我需要以更复杂的方式准备文件流-在需要时向这些流添加新行字符。这将使解决方案变得有点长。但这是完全可能的。

更新2:

正如你在评论中提到的那样,lineReader会继续遍历即使已经找到所需的行,这会拖慢应用程序。我认为我们可以像这样停止它:

lineReader.on('line', function(line) {
  lineNumber++;
  if (lineNumber >= n && lineNumber < m) {
    func(line, lineNumber);
  }

接下来的三行代码应该会“很快”地停止lineReader,但正如官方文档中所解释的那样,不会立即停止。

  if (lineNumber > m) {
    lineReader.close();
  }
});

我相信这应该可以解决问题。

我会测量“行消耗”部分所需的时间 - 也许它没有被优化?否则,我认为使用 readline 读取文件并不会过于缓慢。我会尝试使用无操作 func = () => {},看看是否仍然很慢... - Hero Qu
@plexus 你解决了吗?如果慢是由于readline函数,那么我目前能想到的唯一加快速度的方法是:1)找到第N行和第M+1行(或第M行结束)开始的位置,并以最简化的方式完成,然后2)创建读取流,偏移量等于第N行的起始位置,从而跳过readline发出的所有先前行的内容,然后3)像之前一样使用readline消耗这样缩短的流。 - Hero Qu
但是如果之前的行长度不固定,如何跳过所有之前的行呢?我无法想出解决方法,所以我考虑将大型数据集拆分为多个子集。 - plexus
我的意思是首先找到文件中第N行开始的字节的绝对位置。然后,使用偏移量等于找到的字节数来初始化readFileStream。就好像我们有一个从第N行开始的文件,这样我们就不会浪费时间通过readline遍历从第一行到第(n-1)行。这将节省一些时间,前提是找到此偏移量比使用readline功能从文件开头开始遍历更快。那只是一个想法。 - Hero Qu
你尝试过使用 func = () => {} 并查看它是否能快速工作吗? - Hero Qu
显示剩余4条评论

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