Cara membaca dari stdin baris demi baris di Node

177

Saya mencari untuk memproses file teks dengan simpul menggunakan panggilan baris perintah seperti:

node app.js < input.txt

Setiap baris file perlu diproses secara individual, tetapi begitu diproses, baris input dapat dilupakan.

Menggunakan pendengar on-data dari stdin, saya mendapatkan input steam yang dipotong oleh ukuran byte jadi saya mengaturnya.

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

Tapi ini sepertinya sangat ceroboh. Harus memijat sekitar item pertama dan terakhir dari array garis. Apakah tidak ada cara yang lebih elegan untuk melakukan ini?

Matt R. Wilson
sumber

Jawaban:

207

Anda dapat menggunakan modul readline untuk membaca dari stdin baris demi baris:

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

rl.on('line', function(line){
    console.log(line);
})
pungutan
sumber
3
Itu tampaknya bekerja dengan baik untuk memasukkan input dengan tangan di konsol, namun, ketika saya mengirimkan file ke perintah file dikirim ke stdout. Bug? readline dianggap tidak stabil pada saat ini.
Matt R. Wilson
1
Saya pikir Anda hanya dapat mengubah process.stdoutke aliran tulisan yang berbeda - bisa sesederhanaoutput: new require('stream').Writable()
Jeff Sisson
3
Sayangnya, saya butuh stdout. Saya meninggalkannya dari pertanyaan saya, tetapi saya mencoba agar aplikasi tersebut dapat digunakan sebagai node app.js < input.txt > output.txt.
Matt R. Wilson
Rupanya ini adalah 'dengan desain' github.com/joyent/node/issues/4243#issuecomment-10133900 . Jadi saya akhirnya melakukan seperti yang Anda katakan dan memberikan opsi output aliran tulisan dummy, kemudian menulis langsung ke aliran stdout. Saya tidak suka, tapi itu berhasil.
Matt R. Wilson
13
Sepertinya jika Anda meneruskan argumen terminal: falseuntuk membuat Antarmuka, itu memperbaiki masalah ini.
jasoncrawford
61
// Work on POSIX and Windows
var fs = require("fs");
var stdinBuffer = fs.readFileSync(0); // STDIN_FILENO = 0
console.log(stdinBuffer.toString());
Zelika
sumber
3
Bisakah Anda memasukkan beberapa detail? Sudah ada jawaban yang diterima dengan nilai tinggi
jhhoff02
2
Ini tidak berfungsi untuk saya (simpul v9.2.0, Windows). Error: EISDIR: illegal operation on a directory, fstat at tryStatSync (fs.js: 534: 13) `
AlexChaffee
2
Bekerja untuk saya pada simpul v6.11.2, OSX.
tiffon
3
@AlexChaffee: Tampaknya ada bug di Windows (masih ada pada v9.10.1) jika tidak ada input stdin atau jika stdin ditutup - lihat masalah GitHub ini . Terlepas dari ini, bagaimanapun, solusi tidak bekerja pada Windows.
mklement0
3
bekerja dengan sangat baik dan merupakan yang terpendek sejauh ini, bisa membuatnya lebih pendek dengan melakukanfs.readFileSync(0).toString()
localhostdotdev
56

readlinedirancang khusus untuk bekerja dengan terminal (yaitu process.stdin.isTTY === true). Ada banyak modul yang menyediakan fungsionalitas split untuk stream umum, seperti split . Itu membuat segalanya sangat mudah:

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

function processLine (line) {
  console.log(line + '!')
}
vkurchatkin
sumber
6
tidak bukan. Jika Anda tidak ingin membaca baris demi baris, Anda tidak memerlukannya sama sekali
vkurchatkin
6
Kiat: jika Anda ingin menjalankan beberapa kode setelah memproses semua baris, tambahkan .on('end', doMoreStuff)setelah yang pertama .on(). Ingat bahwa jika Anda hanya menulis kode secara normal setelah pernyataan dengan .on(), kode itu akan berjalan sebelum input apa pun dibaca, karena JavaScript tidak sinkron.
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
sumber
0

berbagi untuk orang lain:

baca stream baris demi baris, harus bagus untuk file besar yang disalurkan ke stdin, versi saya:

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
sumber
-1

Dalam kasus saya program (elinks) mengembalikan baris yang tampak kosong, tetapi sebenarnya memiliki karakter terminal khusus, kode kontrol warna dan backspace, sehingga grepopsi yang disajikan dalam jawaban lain tidak bekerja untuk saya. Jadi saya menulis skrip kecil ini di Node.js. Saya menelepon file itu tight, tapi itu hanya nama acak.

#!/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
sumber