Javascript Node.js:计算文件中的行数

Javascript Node.js:计算文件中的行数,javascript,node.js,Javascript,Node.js,我有大的文本文件,范围在30MB和10GB之间。如何使用Node.js计算文件中的行数 我有以下限制: 整个文件不需要写入内存 执行该任务不需要子进程 您可以使用wc var exec = require('child_process').exec; exec('wc /path/to/file', function (error, results) { console.log(results); }); 不使用wc的解决方案: var i; var count = 0; requ

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

我有以下限制:

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

您可以使用
wc

var exec = require('child_process').exec;

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

不使用wc的解决方案:

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);
  });

自从iojs 1.5.0以来,就有了
Buffer#indexOf()
方法,使用它与Andrey Sidorov的答案进行比较:

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:~$ 
ubuntu@server:~$wc日志
7342500 27548750 427155000原木
ubuntu@server:~$time wc-l日志
7342500原木
实际0.180s
用户0m0.088s
系统0m0.084s
ubuntu@server:~$nvm使用节点
现在使用节点v0.12.1
ubuntu@server:~$time节点countlines.js日志
7342500
实际0m2.559s
用户0m2.200s
sys 0m0.340s
ubuntu@server:~$nvm使用iojs
现在使用节点iojs-v1.6.2
ubuntu@server:~$time iojs countlines2.js日志
7342500
实0m1.363s
用户0m0.920s
系统0m0.424s
ubuntu@server:~$cat countlines.js
var i;
var计数=0;
require('fs').createReadStream(process.argv[2])
.on('data',函数(块){
对于(i=0;i-1)计数++
})
.on('end',function(){
控制台日志(计数);
});
ubuntu@server:~$ 

这里是另一种不用太多嵌套的方法

var fs = require('fs');
filePath = process.argv[2];
fileBuffer =  fs.readFileSync(filePath);
to_string = fileBuffer.toString();
split_lines = to_string.split("\n");
console.log(split_lines.length-1);
也可以使用indexOf():


有一个名为npm的模块。我一直在使用smallish(我们可以使用它让VM找到换行符:

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
查找第一个换行符的位置。它增加
行数
,然后查找下一个位置。
.indexOf
的第二个参数告诉我们从何处开始查找换行符。这样我们就跳过了缓冲区的大块。while循环将运行0每行换行一次,再加一次

我们让节点运行时为我们执行搜索,这是在较低级别上实现的,应该更快


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

我找到的最佳解决方案是使用Promission、async和await。这也是Wait如何履行承诺的一个示例:

#!/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();

如果您使用节点8及更高版本,则可以使用此异步/等待模式

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' });
}


wc-l文件
…“使用NodeJS”--这个要求背后有什么真正的技术原因吗?我确信
wc
比任何“本机”都要快nodejs解决方案您可以数一数行--@zerkms您使用的是哪种shell脚本语言?您的基准测试不是很有说服力,因为您运行的文件不是结构化的行,因此不能代表OP想要处理的文件类型。行
if(chunk[i]=10)count++;
在分析文本文件时比分析二进制视频文件时执行的频率要高得多。我没有100mb的文本文件:)即使是类似的100mb文本文件,但换行数为10倍,我也不期望有任何不同——这是相同的线性搜索,迭代每个缓冲区块中的每个字节。这说明我是无辜的,但“chunk[I]==10”是什么意思?我猜如果区块等于10,那就是一个新行,但是为什么要和数字10比较呢?10是“新行”字符的ascii码。为了更好的可读性,您可以提前几行
const LINE\u FEED='\n'.charCodeAt(0)
,然后
if(chunk[i]==LINE\u FEED)count++
您的实现被关闭了一次。例如,如果您的文件有两行,那么它只有一行新行,因此您的脚本将记录
1
wc
是一个特定于bash的命令,并且可能无法在windows环境中工作,例如
wc-l
仅计算行数
wc-l path/to/file
将给出行数和文件名。要仅获取行数,请使用
wc-l
如果您喜欢这样做,请尝试
sed-n'$='/path/To/file
它只返回行数,您可以在其上应用函数
parseInt
来获取您的行数。
parseInt(execSync('wc-l
虽然这个代码片段可以解决这个问题,但它确实有助于提高文章的质量。请记住,您将在将来回答读者的问题,这些人可能不知道您的代码建议的原因。还请尽量不要用解释性注释挤满你的代码,这会降低代码和解释的可读性!此解决方案需要将文件加载到内存中。我建议不要这样做。使用
wc
的答案并不正确,因为
wc
优化了文件流。与一年前发布相同内容的人相比,答案也没有增加任何有价值的内容。问题特别指出,文件的大小从30MB到10GB不等。此解决方案在处理之前将整个文件读入内存。这可能会导致代码崩溃,因为JavaScript可能会耗尽内存。与这里展示的其他解决方案相比,这是最好的解决方案!这个答案太神奇了。应该在上面。计算200MB文件上的行数只花了200毫秒。我可以很高兴地确认,这种方法在非常大的文件(16GB)上也表现得非常好。如果有人能从第
行开始解释它是如何工作的,那么让idx=-1
就太棒了。非常感谢。对于10gb文件,至少可以说这不是很好的性能。这是一个简单而好的方法,但只适用于小文件!如果文件是10GB文件,请使用Scrip
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);
  });
};
#!/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();
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' });
}