2012-09-17 12 views
17

Tengo archivos de texto grandes, que oscilan entre 30MB y 10GB. ¿Cómo puedo contar el número de líneas en un archivo usando Node.js?Node.js: Cuente el número de líneas en un archivo

tengo estas limitaciones:

  • El archivo completo no necesita ser escrito en la memoria
  • Un proceso hijo no está obligado a realizar la tarea
+5

'wc -l archivo' ... – zerkms

+0

" using NodeJS "- ¿alguna razón técnica real detrás de este requisito? – zerkms

+2

Estoy seguro de que 'wc' será más rápido que cualquier solución nodejs" nativa " – zerkms

Respuesta

22

solución sin nosotros ing 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); 
    }); 

es más lento, pero no tanto se podría esperar - 0,6 s para 140M + archivo incluyendo el tiempo de carga Node.js & inicio

>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 
+3

Su punto de referencia no es muy convincente ya que lo está ejecutando en un archivo * no * estructurado en líneas y, como tal, no es representativo del tipo de archivo que el OP desea procesar. La línea 'if (fragmento [i] == 10) count ++;' se ejecutará mucho más a menudo durante el análisis de un archivo de texto que durante el análisis de un archivo de video binario. – ebohlman

+0

No tengo archivo de texto de 100mb :) Y no espero ninguna diferencia incluso en el caso de archivos de texto de 100mb pero con 10x número de líneas nuevas: es la misma búsqueda lineal que itera cada byte en cada uno de los fragmentos de Buffer –

+0

I replicated ingrese la secuencia de comandos y la concatene a un solo archivo de texto, 1468750000 caracteres, 62500000 líneas. Hora WC: 0m1.375s, hora node.js: 0m6.254s. La misma diferencia de 4.5 veces (que podría ser mejor, pero aún lo suficientemente buena para el programa JS vs C) –

17

Se podría hacer esto como los comentarios sugieren usar wc

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

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

'wc' es un comando específico de bash y podría no funcionar en un entorno de Windows, por ejemplo – Renaud

+1

' wc -l' para contar solo el número de líneas –

+1

'wc -l ruta/a/archivo' da el número de líneas junto con el nombre del archivo. Para obtener solo el número de líneas use 'wc -l Sarita

1

desde iojs 1.5.0 no es Buffer#indexOf() método , usándolo para compararlo con la respuesta de Andrey Sidorov:

[email protected]:~$ wc logs 
    7342500 27548750 427155000 logs 
[email protected]:~$ time wc -l logs 
7342500 logs 

real 0m0.180s 
user 0m0.088s 
sys 0m0.084s 
[email protected]:~$ nvm use node 
Now using node v0.12.1 
[email protected]:~$ time node countlines.js logs 
7342500 

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

real 0m1.363s 
user 0m0.920s 
sys 0m0.424s 
[email protected]:~$ 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); 
    }); 
[email protected]:~$ 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); 
    }); 
[email protected]:~$ 
3

Aquí hay otra forma de anidar.

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); 
+0

Para un archivo de 10 gb, esto no es muy efectivo, por decir lo menos. –

1

También puede usar indexOf():

var index = -1; 
var count = 0; 
while ((index = chunk.indexOf(10, index + 1)) > -1) count++; 
3
var fs=require('fs'); 
filename=process.argv[2]; 
var data=fs.readFileSync(filename); 
var res=data.toString().split('\n').length; 
console.log(res-1);` 
+1

Si bien este fragmento de código puede resolver la pregunta, [incluyendo una explicación] (https://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) realmente ayuda a mejorar la calidad de su publicación. Recuerde que usted está respondiendo la pregunta a los lectores en el futuro, y es posible que esas personas no sepan los motivos de su sugerencia de código. Por favor, intente no saturar su código con comentarios explicativos, ¡esto reduce la legibilidad tanto del código como de las explicaciones! –

+0

Esta solución requiere cargar el archivo en la memoria. Aconsejaría en contra de eso. La respuesta usando 'wc' no se debe a que' wc' está optimizado para transmitir el archivo. –

+0

La respuesta tampoco agrega nada valioso en comparación con [Alan Viars] (https://stackoverflow.com/a/32286822/238978) que publicó lo mismo un año antes. –

1

Hay un módulo de NPM llamada count-lines-in-file. Lo he estado usando para archivos pequeños (< 1000 líneas) y hasta ahora ha funcionado muy bien.

3

Podemos utilizar indexOf dejar que la máquina virtual encontrar las nuevas líneas:

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

Lo que esto hace es que la solución se encuentra la posición de la primera línea nueva usando .indexOf. Incrementa lineCount, luego encuentra la siguiente posición. El segundo parámetro para .indexOf indica dónde comenzar a buscar nuevas líneas. De esta forma saltamos sobre grandes porciones del buffer. El ciclo while se ejecutará una vez por cada nueva línea, más uno.

Estamos dejando que el tiempo de ejecución del nodo haga la búsqueda para nosotros, que se implementa en un nivel inferior y debe ser más rápido.

En mi sistema, esto es aproximadamente el doble de rápido que ejecutar un bucle for sobre la longitud del búfer en un archivo grande (111 MB).

+1

¡Esta es la mejor solución comparada con otras que se muestran aquí! – loretoparisi

Cuestiones relacionadas