Come leggere da stdin riga per riga in Nodo


177

Sto cercando di elaborare un file di testo con nodo usando una chiamata da riga di comando come:

node app.js < input.txt

Ogni riga del file deve essere elaborata singolarmente, ma una volta elaborata la riga di input può essere dimenticata.

Usando il listener on-data dello stdin, ricevo il vapore di input tagliato da una dimensione di byte, quindi lo imposto.

process.stdin.resume();
process.stdin.setEncoding('utf8');

var lingeringLine = "";

process.stdin.on('data', function(chunk) {
    lines = chunk.split("\n");

    lines[0] = lingeringLine + lines[0];
    lingeringLine = lines.pop();

    lines.forEach(processLine);
});

process.stdin.on('end', function() {
    processLine(lingeringLine);
});

Ma questo sembra così sciatto. Dover massaggiare attorno al primo e all'ultimo elemento dell'array line. Non esiste un modo più elegante per farlo?

Risposte:


207

Puoi usare il modulo readline per leggere dallo stdin riga per riga:

var readline = require('readline');
var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});

rl.on('line', function(line){
    console.log(line);
})

3
Ciò sembra funzionare bene per l'immissione manuale della console nella console, tuttavia, quando passo un file nel comando, il file viene inviato a stdout. Un insetto? readline è considerato instabile a questo punto.
Matt R. Wilson,

1
Penso che puoi semplicemente passare process.stdouta un altro flusso scrivibile - potrebbe essere semplice comeoutput: new require('stream').Writable()
Jeff Sisson

3
Sfortunatamente, ho bisogno dello stdout. L'ho lasciato fuori dalla mia domanda, ma sto cercando di rendere l'app utilizzabile come node app.js < input.txt > output.txt.
Matt R. Wilson,

Apparentemente si tratta di "design" github.com/joyent/node/issues/4243#issuecomment-10133900 . Così ho finito per fare come hai detto e ho fornito all'opzione di output un flusso scrivibile fittizio, quindi ho scritto direttamente sul flusso stdout. Non mi piace, ma funziona.
Matt R. Wilson,

13
Sembra che se passi l'argomento terminal: falsea createInterface, risolve questo problema.
Jasoncrawford,

61
// Work on POSIX and Windows
var fs = require("fs");
var stdinBuffer = fs.readFileSync(0); // STDIN_FILENO = 0
console.log(stdinBuffer.toString());

3
Potresti includere alcuni dettagli? Esiste già una risposta accettata molto apprezzata
jhhoff02

2
Questo non funziona per me (nodo v9.2.0, Windows). Error: EISDIR: illegal operation on a directory, fstat at tryStatSync (fs.js: 534: 13) `
AlexChaffee,

2
Ha funzionato per me sul nodo v6.11.2, OSX.
Tiffon

3
@AlexChaffee: sembra che ci sia un bug su Windows (ancora presente dalla v9.10.1) se non c'è input stdin o se stdin è chiuso - vedi questo problema di GitHub . Oltre a questo, però, la soluzione fa il lavoro su Windows.
mklement0

3
funziona molto bene ed è di gran lunga il più corto, potrebbe accorciarlo facendofs.readFileSync(0).toString()
localhostdotdev

56

readlineè specificamente progettato per funzionare con il terminale (ovvero process.stdin.isTTY === true). Esistono molti moduli che forniscono funzionalità split per flussi generici, come split . Rende le cose semplicissime:

process.stdin.pipe(require('split')()).on('data', processLine)

function processLine (line) {
  console.log(line + '!')
}

6
no non lo è. Se non vuoi leggere riga per riga non ne hai bisogno affatto
vkurchatkin

6
Suggerimento: se si desidera eseguire del codice dopo aver elaborato tutte le righe, aggiungere .on('end', doMoreStuff)dopo il primo .on(). Ricorda che se scrivi semplicemente il codice normalmente dopo l'istruzione con .on(), quel codice verrà eseguito prima della lettura di qualsiasi input, perché JavaScript non è sincrono.
Rory O'Kane,

14
#!/usr/bin/env node

const EventEmitter = require('events');

function stdinLineByLine() {
  const stdin = new EventEmitter();
  let buff = "";

  process.stdin
    .on('data', data => {
      buff += data;
      lines = buff.split(/[\r\n|\n]/);
      buff = lines.pop();
      lines.forEach(line => stdin.emit('line', line));
    })
    .on('end', () => {
      if (buff.length > 0) stdin.emit('line', buff);
    });

  return stdin;
}

const stdin = stdinLineByLine();
stdin.on('line', console.log);

0

condividere per gli altri:

leggere lo stream riga per riga, dovrebbe essere buono per file di grandi dimensioni convogliati in stdin, la mia versione:

var n=0;
function on_line(line,cb)
{
    ////one each line
    console.log(n++,"line ",line);
    return cb();
    ////end of one each line
}

var fs = require('fs');
var readStream = fs.createReadStream('all_titles.txt');
//var readStream = process.stdin;
readStream.pause();
readStream.setEncoding('utf8');

var buffer=[];
readStream.on('data', (chunk) => {
    const newlines=/[\r\n]+/;
    var lines=chunk.split(newlines)
    if(lines.length==1)
    {
        buffer.push(lines[0]);
        return;
    }   

    buffer.push(lines[0]);
    var str=buffer.join('');
    buffer.length=0;
    readStream.pause();

    on_line(str,()=>{
        var i=1,l=lines.length-1;
        i--;
        function while_next()
        {
            i++;
            if(i<l)
            {
                return on_line(lines[i],while_next);
            }
            else
            {
                buffer.push(lines.pop());
                lines.length=0;
                return readStream.resume();
            }
        }
        while_next();
    });
  }).on('end', ()=>{
      if(buffer.length)
          var str=buffer.join('');
          buffer.length=0;
        on_line(str,()=>{
            ////after end
            console.error('done')
            ////end after end
        });
  });
readStream.resume();

-1

Nel mio caso il programma (elinks) ha restituito righe che sembravano vuote, ma in realtà avevano caratteri terminali speciali, codici di controllo del colore e backspace, quindi le grepopzioni presentate in altre risposte non funzionavano per me. Quindi ho scritto questa piccola sceneggiatura in Node.js. Ho chiamato il file tight, ma è solo un nome casuale.

#!/usr/bin/env node

function visible(a) {
    var R  =  ''
    for (var i = 0; i < a.length; i++) {
        if (a[i] == '\b') {  R -= 1; continue; }  
        if (a[i] == '\u001b') {
            while (a[i] != 'm' && i < a.length) i++
            if (a[i] == undefined) break
        }
        else R += a[i]
    }
    return  R
}

function empty(a) {
    a = visible(a)
    for (var i = 0; i < a.length; i++) {
        if (a[i] != ' ') return false
    }
    return  true
}

var readline = require('readline')
var rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false })

rl.on('line', function(line) {
    if (!empty(line)) console.log(line) 
})
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.