Cách đọc từ stdin từng dòng trong Node


177

Tôi đang tìm cách xử lý tệp văn bản với nút bằng cách sử dụng lệnh gọi dòng lệnh như:

node app.js < input.txt

Mỗi dòng của tệp cần được xử lý riêng lẻ, nhưng sau khi xử lý, dòng đầu vào có thể bị quên.

Sử dụng trình nghe trên dữ liệu của stdin, tôi nhận được hơi đầu vào được chia theo kích thước byte nên tôi thiết lập nó.

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

Nhưng điều này có vẻ rất cẩu thả. Phải mát xa xung quanh các mục đầu tiên và cuối cùng của mảng dòng. Không có cách nào thanh lịch hơn để làm điều này?

Câu trả lời:


207

Bạn có thể sử dụng mô-đun readline để đọc từ dòng stdin theo dòng:

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
Điều đó dường như hoạt động tốt để nhập đầu vào bằng tay trong bàn điều khiển, tuy nhiên, khi tôi chuyển một tệp vào lệnh, tệp sẽ được gửi đến thiết bị xuất chuẩn. Một lỗi? đường đọc được coi là không ổn định tại thời điểm này.
Matt R. Wilson

1
Tôi nghĩ bạn chỉ có thể thay đổi process.stdoutthành một luồng có thể ghi khác - nó có thể đơn giản nhưoutput: new require('stream').Writable()
Jeff Sisson

3
Thật không may, tôi cần thiết bị xuất chuẩn. Tôi đã bỏ nó ra khỏi câu hỏi của mình, nhưng tôi đang cố gắng để ứng dụng có thể sử dụng được node app.js < input.txt > output.txt.
Matt R. Wilson

Rõ ràng đây là 'theo thiết kế' github.com/joyent/node/issues/4243#issuecomment-10133900 . Vì vậy, tôi đã kết thúc việc làm như bạn nói và cung cấp tùy chọn đầu ra một luồng có thể ghi giả, sau đó viết trực tiếp vào luồng xuất chuẩn. Tôi không thích nó, nhưng nó hoạt động.
Matt R. Wilson

13
Có vẻ như nếu bạn truyền đối số terminal: falsecho createdInterface, nó sẽ khắc phục vấn đề này.
jasoncrawford

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

3
Bạn có thể bao gồm một số chi tiết? Hiện đã có câu trả lời được đánh giá cao
jhhoff02

2
Điều này không làm việc cho tôi (nút v9.2.0, Windows). Error: EISDIR: illegal operation on a directory, fstat at tryStatSync (fs.js: 534: 13) `
AlexChaffee

2
Làm việc cho tôi trên nút v6.11.2, OSX.
18 giờ 44 phút

3
@AlexChaffee: Dường như có một lỗi trên Windows (vẫn xuất hiện kể từ phiên bản 9.10.1) nếu không có đầu vào stdin hoặc nếu stdin bị đóng - hãy xem vấn đề GitHub này . Ngoài ra, giải pháp này không hoạt động trên Windows.
mkuity0

3
hoạt động rất tốt và ngắn nhất cho đến nay, có thể làm cho nó ngắn hơn bằng cách thực hiệnfs.readFileSync(0).toString()
localhostdotdev

56

readlineđược thiết kế đặc biệt để làm việc với thiết bị đầu cuối (đó là process.stdin.isTTY === true). Có rất nhiều mô-đun cung cấp chức năng phân chia cho các luồng chung, như phân chia . Nó làm cho mọi thứ siêu dễ dàng:

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

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

6
không, không phải vậy. Nếu bạn không muốn đọc line-by-line bạn không cần gì cả
vkurchatkin

6
Mẹo: nếu bạn muốn chạy một số mã sau khi xử lý tất cả các dòng, hãy thêm .on('end', doMoreStuff)vào sau dòng đầu tiên .on(). Hãy nhớ rằng nếu bạn chỉ viết mã bình thường sau câu lệnh .on(), mã đó sẽ chạy trước khi bất kỳ đầu vào nào được đọc, bởi vì JavaScript không đồng bộ.
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

chia sẻ cho người khác:

đọc từng dòng một, nên tốt cho các tệp lớn được chuyển vào stdin, phiên bản của tôi:

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

Trong trường hợp của tôi, chương trình (elinks) trả về các dòng trông trống rỗng, nhưng trên thực tế có các ký tự đầu cuối đặc biệt, mã điều khiển màu và khoảng lùi, vì vậy grepcác tùy chọn được trình bày trong các câu trả lời khác không phù hợp với tôi. Vì vậy, tôi đã viết kịch bản nhỏ này trong Node.js. Tôi đã gọi tập tin tight, nhưng đó chỉ là một cái tên ngẫu nhiên.

#!/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) 
})
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.