2012-09-17 13 views
17

I file di testo di grandi dimensioni sono compresi tra 30MB e 10GB. Come posso contare il numero di righe in un file usando Node.js?Node.js: conta il numero di righe in un file

ho queste limitazioni:

  • L'intero file non ha bisogno di essere scritto nella memoria
  • Un processo figlio non è tenuto a svolgere il compito
+5

'wc -l file' ... – zerkms

+0

" using NodeJS "- qualsiasi motivo tecnico reale dietro questo requisito? – zerkms

+2

Sono sicuro che 'wc' sarà più veloce di qualsiasi soluzione nodejs" nativa " – zerkms

risposta

22

senza di noi 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); 
    }); 

è più lento, ma non così tanto ci si potrebbe aspettare - 0.6s per 140M + di file compreso il tempo di caricamento node.js & avvio

>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

Il tuo benchmark non è molto convincente dal momento che lo stai eseguendo su un file che * non * è strutturato in linee e in quanto tale non è rappresentativo del tipo di file che l'OP vuole elaborare. La riga 'if (chunk [i] == 10) count ++;' verrà eseguita molto più spesso durante l'analisi di un file di testo che durante l'analisi di un file video binario. – ebohlman

+0

Non ho file di testo 100mb :) E non mi aspetto alcuna differenza nemmeno nel caso di file di testo 100mb simili ma con 10x numero di newline - è la stessa ricerca lineare che itera ogni byte in ciascuno dei blocchi Buffer –

+0

I replicati inserire lo script stesso e concatenarlo in un unico file di testo, 1468750000 caratteri, 62500000 righe. Ora WC: 0m1.375s, tempo node.js: 0m6.254s. Stessa differenza di 4,5 volte (che potrebbe essere migliore, ma comunque sufficiente per il programma JS vs C) –

17

Si potrebbe fare questo come suggeriscono i commenti utilizzando la soluzione wc

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

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

'wc' è un comando specifico per bash e potrebbe non funzionare in un ambiente Windows ad esempio – Renaud

+1

' wc -l' per contare solo il numero di righe –

+1

'wc -l percorso/su/file' sarà indica il numero di linee insieme al nome del file. Per ottenere solo un numero di righe usa 'wc -l Sarita

1

dal iojs 1.5.0 c'è Buffer#indexOf() metodo , usandolo per confrontare con la risposta di 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

Ecco un altro modo senza così tanto nidificazione.

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

Per un file da 10 gb, questo non è molto performante, per non dire altro. –

1

è possibile utilizzare anche 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

Anche se questo snippet di codice può risolvere la domanda, [compresa una spiegazione] (https://meta.stackexchange.com/questions/114762/explaining-ident-based-ansed-answers) aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro, e queste persone potrebbero non conoscere le ragioni del tuo suggerimento sul codice. Cerca anche di non affollare il tuo codice con commenti esplicativi, questo riduce la leggibilità sia del codice che delle spiegazioni! –

+0

Questa soluzione richiede il caricamento del file in memoria. Lo sconsiglierei. La risposta usando 'wc' non lo fa perché' wc' è ottimizzato per lo streaming del file. –

+0

La risposta inoltre non aggiunge nulla di valore rispetto a [Alan Viars] (https://stackoverflow.com/a/32286822/238978) che ha pubblicato la stessa cosa un anno prima. –

1

C'è un modulo NPM chiamato count-lines-in-file. L'ho usato per file piccoli (< righe) e ha funzionato benissimo finora.

3

Possiamo usare indexOf di lasciare che il VM trovare i ritorni a capo:

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

Cosa questa soluzione non è che si trova la posizione del primo ritorno a capo usando .indexOf. Incrementa lineCount, quindi trova la posizione successiva. Il secondo parametro su .indexOf indica dove iniziare a cercare le nuove linee. In questo modo stiamo saltando su grandi blocchi del buffer. Il ciclo while verrà eseguito una volta per ogni nuova riga, più una.

Stiamo facendo in modo che il runtime del nodo esegua la ricerca che è implementato a un livello inferiore e dovrebbe essere più veloce.

Sul mio sistema questo è circa il doppio della velocità di un ciclo for sulla lunghezza del buffer su un file di grandi dimensioni (111 MB).

+1

Questa è la soluzione migliore rispetto ad altre mostrate qui! – loretoparisi

Problemi correlati