2016-06-29 36 views
11

Ho già appreso che readline può essere utilizzato per leggere il file riga per riga, ad es.File di lettura efficiente riga per riga nel nodo

readline 
    .createInterface({input: fs.createReadStream('xxx')}) 
    .on('line', (line) => { apply_regexp_on_line }) 
    .on('close',() => { report_all_regexps }); 

Tuttavia, questo è piuttosto lento, dal momento che ho confrontato le prestazioni di grep e regexp JavaScript, e quest'ultimo ha prestazioni migliori sulle espressioni regolari che ho provato. (vedi benchmark) Quindi penso di dover incolpare la lettura asincrona del nodo.

Nella mia situazione, non mi interessa affatto asincrono, ho solo bisogno di sfruttare la regexp veloce da JavaScript per elaborare file di registro molto grandi (in genere 1-2 GB, a volte fino a 10 GB). Qual è il modo migliore per fare questo? La mia unica preoccupazione è la velocità.

Punti bonus: alcuni file di registro sono compressi con gzip, quindi è necessario decomprimerli. Se qualcuno mi può consigliare un veloce lettore line-by-line sia per il testo semplice che per il testo gzippato, sarei molto apprezzato.

+0

Non si sa cosa 'apply_regexp_on_line' ma è possibile utilizzare il programma unix' sed' per eseguire sostituzioni di stringa? È abbastanza veloce Probabilmente puoi scrivere uno script di shell semplice e veloce per eseguire la decompressione e il seding. –

+0

Si prega di consultare il link di riferimento nella domanda. 'sed' non è veloce come JavaScript. Fondamentalmente 'apply_regexp_on_line' catturerebbe del testo nel file di log usando regexp e lo memorizzasse, e' report_all_regexps' avrebbe riportato il testo catturato in un dato formato. – xis

+0

grazie per l'informazione. Non mi aspettavo che 'sed' fosse più lento della regex di javascript! argomento votato e aggiunto ai preferiti. Sono ansioso di conoscere anche la soluzione. –

risposta

1

Come si oppongono ai dati?

// module linegrep.js 
'use strict'; 
var through2 = require('through2'); 
var StringDecoder = require('string_decoder').StringDecoder 

function grep(regex) { 
    var decoder = new StringDecoder('utf8'), 
     last = "", 
     lineEnd = /\r?\n/; 

    var stream = through2({}, function transform(chunk, enc, cb) { 
     var lines = decoder.write(last + chunk).split(lineEnd), i; 
     last = lines.pop(); 
     for (i = 0; i < lines.length; i++) { 
      if (regex.test(lines[i])) this.push(lines[i]); 
     } 
     cb(); 
    }, function flush(cb) { 
     if (regex.test(last)) this.push(last); 
     cb(); 
    }); 
    stream._readableState.objectMode = true; 
    return stream; 
} 

module.exports = grep; 

e

// index.js 

'use strict'; 
var fs = require('fs'); 
var zlib = require('zlib'); 
var grep = require('./linegrep'); 

function grepFile(filename, regex) { 
    var rstream = fs.createReadStream(filename, {highWaterMark: 172 * 1024}); 
    if (/\.gz$/.test(filename)) rstream = rstream.pipe(zlib.createGunzip()); 
    return rstream 
     .pipe(grep(regex)); 
} 

// ------------------------------------------------------------------------- 

var t = Date.now(), mc = 0; 
grepFile('input.txt', /boot\.([a-z]+)_head\./).on('data', function (line) { 
    mc++; 
    console.log(line); 
}).on('end', function() { 
    console.log(mc + " matches, " + (Date.now() - t) + " ms"); 
}); 

Questo trasforma un flusso di file in un flusso oggetto di linee, le mappe attraverso la vostra espressione regolare e restituisce solo le linee corrispondenti.

+0

Più lento del mio codice che usa 'fs.readFileSync (filename) .toString(). Split ('\ n'). ForEach ((line) => {regexp. exec (/ regexp /);}); ' – xis

+0

Hm, è un peccato.D'altra parte, può gestire file di input arbitrariamente grandi. – Tomalak

+0

Bene, è corretto. Sto indovinando che il sistema di eventi 'node' è la ragione per cui è lento. Bypassare l'evento renderebbe il codice molto più veloce. – xis

Problemi correlati