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?

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

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

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 + '!')
}

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

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

-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) 
})
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.