Wie man in Node Zeile für Zeile von stdin liest

176

Ich möchte eine Textdatei mit Knoten mithilfe eines Befehlszeilenaufrufs wie folgt verarbeiten:

node app.js < input.txt

Jede Zeile der Datei muss einzeln verarbeitet werden, aber sobald sie verarbeitet wurde, kann die Eingabezeile vergessen werden.

Mit dem On-Data-Listener des STDIN wird der Eingangsdampf um eine Bytegröße aufgeteilt, sodass ich ihn einrichte.

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

Aber das scheint so schlampig. Um das erste und letzte Element des Linienarrays herum massieren müssen. Gibt es nicht einen eleganteren Weg, dies zu tun?

Matt R. Wilson
quelle

Antworten:

207

Mit dem Readline- Modul können Sie Zeile für Zeile von stdin lesen:

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

rl.on('line', function(line){
    console.log(line);
})
levi
quelle
3
Das scheint gut zu funktionieren, um Eingaben von Hand in die Konsole einzugeben. Wenn ich jedoch eine Datei an den Befehl übergebe, wird die Datei an stdout gesendet. Ein Käfer? readline wird an dieser Stelle als instabil angesehen.
Matt R. Wilson
1
Ich denke, Sie können einfach process.stdoutzu einem anderen beschreibbaren Stream wechseln - es könnte so einfach sein wieoutput: new require('stream').Writable()
Jeff Sisson
3
Leider brauche ich das stdout. Ich habe es aus meiner Frage herausgelassen, aber ich versuche, die App als nutzbar zu machen node app.js < input.txt > output.txt.
Matt R. Wilson
Anscheinend ist dies 'by design' github.com/joyent/node/issues/4243#issuecomment-10133900 . Also habe ich getan, was Sie gesagt haben, und der Ausgabeoption einen beschreibbaren Dummy-Stream bereitgestellt und dann direkt in den stdout-Stream geschrieben. Ich mag es nicht, aber es funktioniert.
Matt R. Wilson
13
Wenn Sie das Argument terminal: falsean createInterface übergeben, wird dieses Problem behoben.
Jasoncrawford
61
// Work on POSIX and Windows
var fs = require("fs");
var stdinBuffer = fs.readFileSync(0); // STDIN_FILENO = 0
console.log(stdinBuffer.toString());
Eifrig
quelle
3
Könnten Sie einige Details angeben? Es gibt bereits eine hoch bewertete akzeptierte Antwort
jhhoff02
2
Dies funktioniert bei mir nicht (Knoten v9.2.0, Windows). Error: EISDIR: illegal operation on a directory, fstat at tryStatSync (fs.js: 534: 13) `
AlexChaffee
2
Arbeitete für mich auf Knoten v6.11.2, OSX.
Tiffon
3
@AlexChaffee: Es scheint einen Fehler unter Windows zu geben (der ab Version 9.10.1 noch vorhanden ist), wenn keine stdin-Eingabe vorhanden ist oder wenn stdin geschlossen ist - siehe dieses GitHub-Problem . Abgesehen davon funktioniert die Lösung jedoch unter Windows.
mklement0
3
funktioniert sehr gut und ist bei weitem die kürzeste, könnte es dadurch kürzer machenfs.readFileSync(0).toString()
localhostdotdev
56

readlinewurde speziell für die Arbeit mit Terminals entwickelt process.stdin.isTTY === true. Es gibt viele Module, die Split-Funktionen für generische Streams wie Split bereitstellen . Es macht die Dinge super einfach:

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

function processLine (line) {
  console.log(line + '!')
}
vkurchatkin
quelle
6
Nein, ist es nicht. Wenn Sie nicht Zeile für Zeile lesen möchten, brauchen Sie es überhaupt nicht
vkurchatkin
6
Tipp: Wenn Sie nach der Verarbeitung aller Zeilen Code ausführen möchten, fügen Sie .on('end', doMoreStuff)nach dem ersten hinzu .on(). Denken Sie daran, dass, wenn Sie den Code nur normal nach der Anweisung mit schreiben .on(), dieser Code ausgeführt wird, bevor eine Eingabe gelesen wird, da JavaScript nicht synchron ist.
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);
simonepri
quelle
0

Teilen für andere:

Stream Zeile für Zeile lesen, sollte für große Dateien gut sein, die in stdin, meine Version, weitergeleitet werden:

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();
Shimon Doodkin
quelle
-1

In meinem Fall gab das Programm (elinks) Zeilen zurück, die leer aussahen, aber tatsächlich spezielle Terminalzeichen, Farbkontrollcodes und Rücktaste hatten, sodass grepdie in anderen Antworten dargestellten Optionen für mich nicht funktionierten. Also habe ich dieses kleine Skript in Node.js geschrieben. Ich habe die Datei angerufen tight, aber das ist nur ein zufälliger Name.

#!/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) 
})
exebook
quelle