Nœud.js: Compter le nombre de lignes dans un fichier

j'ai de gros fichiers texte, qui vont de 30MB et 10GB. Comment puis-je compter le nombre de lignes dans un fichier en utilisant Node.js?

j'ai ces limitations:

  • Le fichier entier n'a pas besoin d'être écrit à la mémoire
  • Un processus enfant n'est pas requis pour effectuer la tâche
22
demandé sur hexacyanide 2012-09-17 08:16:11

9 réponses

solution sans utiliser le 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);
  });

c'est plus lent, mais pas autant qu'on pourrait s'y attendre - 0.6 s pour le fichier 140M+ incluant le noeud.temps de chargement et de démarrage de js

>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
25
répondu Andrey Sidorov 2012-09-17 05:31:42

vous pouvez le faire comme le suggèrent les commentaires en utilisant wc

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

exec('wc /path/to/file', function (error, results) {
    console.log(results);
});
24
répondu Menztrual 2012-09-17 04:29:27

nous pouvons utiliser indexOf pour la VM trouver les retours à la ligne:

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

ce que cette solution fait est qu'elle trouve la position de la première ligne en utilisant .indexOf. Il incrémente lineCount, puis il trouve la position suivante. Le deuxième paramètre .indexOf indique où commencer à chercher des nouvelles lignes. De cette façon, nous sautons par-dessus de gros morceaux de la zone tampon. La boucle while s'exécute une fois pour chaque nouvelle ligne, plus une.

nous laissons l'exécution du noeud faire recherche pour nous, qui est mis en œuvre à un niveau inférieur et devrait être plus rapide.

Sur mon système, c'est environ deux fois plus rapide que l'exécution d'un for boucle sur la longueur du tampon sur un gros fichier (111 Mo).

9
répondu Emil Vikström 2017-01-03 09:10:02

depuis iojs 1.5.0 il y a Buffer#indexOf() méthode, en l'utilisant pour comparer à la réponse D'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:~$ 
3
répondu undoZen 2015-03-27 00:47:06
var fs=require('fs');
filename=process.argv[2];
var data=fs.readFileSync(filename);
var res=data.toString().split('\n').length;
console.log(res-1);`
3
répondu ruchi gupta 2016-06-07 21:54:06

Voici une autre façon sans autant de nidification.

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);
2
répondu Alan Viars 2015-08-29 13:57:31

vous pouvez aussi utiliser indexOf ():

var index = -1;
var count = 0;
while ((index = chunk.indexOf(10, index + 1)) > -1) count++;
1
répondu Jeff Kilbride 2015-11-25 19:22:17

il y a un module npm appelé comte-lignes-de-fichier. Je l'ai utilisé pour les petits fichiers (<1000 lignes) et ça a très bien fonctionné jusqu'à présent.

1
répondu Dom Vinyard 2016-06-29 10:29:08

La meilleure solution que j'ai trouvée est d'utiliser promises et async. C'est aussi un exemple de la façon de transformer une fonction async en synchrone:

#!/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();
0
répondu David Dombrowsky 2018-04-25 19:52:47