关于javascript:Node.js:计算文件中的行数

Node.js: Count the number of lines in a file

我有大文本文件,范围在30MB10GB之间。 如何使用Node.js计算文件中的行数?

我有这些限制:

  • 整个文件不需要写入内存
  • 执行任务不需要子进程


不使用wc的解决方案:

1
2
3
4
5
6
7
8
9
10
var i;
var count = 0;
require('fs').createReadStream(process.argv[2])
  .on('data', function(chunk) {
    for (i=0; i < chunk.length; ++i)
      if (chunk[i] == 10) count++;
  })
  .on('end', function() {
    console.log(count);
  });

它的速度较慢,但??不是那么多 - 140M +文件的0.6s包括node.js loading&amp;启动时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>time node countlines.js video.mp4
619643

real    0m0.614s
user    0m0.489s
sys 0m0.132s

>time wc -l video.mp4
619643 video.mp4
real    0m0.133s
user    0m0.108s
sys 0m0.024s

>wc -c video.mp4
144681406  video.mp4


你可以这样做,因为评论建议使用wc

1
2
3
4
5
var exec = require('child_process').exec;

exec('wc /path/to/file', function (error, results) {
    console.log(results);
});


我们可以使用indexOf让VM找到换行符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function countFileLines(filePath){
  return new Promise((resolve, reject) => {
  let lineCount = 0;
  fs.createReadStream(filePath)
    .on("data", (buffer) => {
      let idx = -1;
      lineCount--; // Because the loop will run once for idx=-1
      do {
        idx = buffer.indexOf(10, idx+1);
        lineCount++;
      } while (idx !== -1);
    }).on("end", () => {
      resolve(lineCount);
    }).on("error", reject);
  });
};

这个解决方案的作用是它使用.indexOf找到第一个换行符的位置。它递增lineCount,然后找到下一个位置。 .indexOf的第二个参数告诉从哪里开始寻找换行符。这样我们就跳过缓冲区的大块。 while循环将为每个换行符加一次。

我们让Node运行时搜索我们,这是在较低级别实现的,应该更快。

在我的系统上,这大约是在大文件(111 MB)上在缓冲区长度上运行for循环的速度的两倍。


因为iojs 1.5.0有Buffer#indexOf()方法,用它来比较Andrey Sidorov的回答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
ubuntu@server:~$ wc logs
  7342500  27548750 427155000 logs
ubuntu@server:~$ time wc -l logs
7342500 logs

real    0m0.180s
user    0m0.088s
sys 0m0.084s
ubuntu@server:~$ nvm use node
Now using node v0.12.1
ubuntu@server:~$ time node countlines.js logs
7342500

real    0m2.559s
user    0m2.200s
sys 0m0.340s
ubuntu@server:~$ nvm use iojs
Now using node iojs-v1.6.2
ubuntu@server:~$ time iojs countlines2.js logs
7342500

real    0m1.363s
user    0m0.920s
sys 0m0.424s
ubuntu@server:~$ cat countlines.js
var i;
var count = 0;
require('fs').createReadStream(process.argv[2])
  .on('data', function(chunk) {
    for (i=0; i < chunk.length; ++i)
      if (chunk[i] == 10) count++;
  })
  .on('end', function() {
    console.log(count);
  });
ubuntu@server:~$ cat countlines2.js
var i;
var count = 0;
require('fs').createReadStream(process.argv[2])
  .on('data', function(chunk) {
    var index = -1;
    while((index = chunk.indexOf(10, index + 1)) > -1) count++
  })
  .on('end', function() {
    console.log(count);
  });
ubuntu@server:~$

如果使用节点8及更高版本,则可以使用此async / await模式

1
2
3
4
5
6
7
8
9
10
11
12
13
const util = require('util');
const exec = util.promisify(require('child_process').exec);

async function fileLineCount({ fileLocation }) {
  const { stdout } = await exec(`cat ${fileLocation} | wc -l`);
  return parseInt(stdout);
};

// Usage

async someFunction() {
  const lineCount = await fileLineCount({ fileLocation: 'some/file.json' });
}


1
2
3
4
5
6
var fs=require('fs');
filename=process.argv[2];
var data=fs.readFileSync(filename);
var res=data.toString().split('
'
).length;
console.log(res-1);`


我发现的最佳解决方案是使用promises,async和await。这也是一个如何等待履行承诺的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env node
const fs = require('fs');
const readline = require('readline');
function main() {
    function doRead() {
        return new Promise(resolve => {
            var inf = readline.createInterface({
                input: fs.createReadStream('async.js'),
                crlfDelay: Infinity
            });
            var count = 0;
            inf.on('line', (line) => {
                console.log(count + ' ' + line);
                count += 1;
            });
            inf.on('close', () => resolve(count));
        });
    }
    async function showRead() {
        var x = await doRead();
        console.log('line count: ' + x);
    }
    showRead();
}
main();


有一个名为count-lines-in-file的npm模块。我一直在使用它的小(<1000行)文件,它到目前为止工作得很好。


您还可以使用indexOf():

1
2
3
var index = -1;
var count = 0;
while ((index = chunk.indexOf(10, index + 1)) > -1) count++;

这是没有那么多嵌套的另一种方式。

1
2
3
4
5
6
7
var fs = require('fs');
filePath = process.argv[2];
fileBuffer =  fs.readFileSync(filePath);
to_string = fileBuffer.toString();
split_lines = to_string.split("
"
);
console.log(split_lines.length-1);