如何在节点中逐行从stdin读取


174

我正在使用以下命令行调用来处理带有node的文本文件:

node app.js < input.txt

文件的每一行都需要单独处理,但是一旦处理完,输入行可能会被忘记。

使用标准输入的数据侦听器,我得到了按字节大小分块的输入流,因此进行了设置。

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

但这似乎草率。必须在行数组的第一项和最后一项周围进行按摩。有没有更优雅的方式做到这一点?

Answers:


205

您可以使用readline模块逐行从stdin进行读取:

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
对于在控制台中手动输入输入来说,这似乎很好用,但是,当我将文件传递给命令时,该文件将发送到stdout。有毛病吗 此时,readline被认为是不稳定的。
Matt R. Wilson

1
我认为您可以更改process.stdout为其他可写流–可以很简单output: new require('stream').Writable()
Jeff Sisson 2013年

3
不幸的是,我需要标准输出。我将其排除在我的问题之外,但我正在尝试使该应用程序可作为使用node app.js < input.txt > output.txt
Matt R. Wilson

显然,这是“设计使然github.com/joyent/node/issues/4243#issuecomment-10133900。因此,我按照您所说的去做,并为输出选项提供了一个可写的虚拟流,然后直接将其写入stdout流。我不喜欢它,但是可以。
Matt R. Wilson

13
看起来,如果您将参数传递terminal: false给createInterface,它将解决此问题。
jasoncrawford 2014年

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

3
您能提供一些细节吗?已经有一个高度接受的答案
jhhoff02

2
这对我不起作用(节点v9.2.0,Windows)。Error: EISDIR: illegal operation on a directory, fstat at tryStatSync(fs.js:534:13)`
AlexChaffee

2
在OSX节点v6.11.2上为我工作。
蒂芬

3
@AlexChaffee:如果没有stdin输入或stdin已关闭,则Windows上似乎存在一个错误(自v9.10.1起仍然存在)-请参见此GitHub问题。但是,除此之外,该解决方案确实可以在Windows上运行。
mklement0 '18 -4-5

3
效果很好,并且是目前为止最短的,可以通过做得更短fs.readFileSync(0).toString()
localhostdotdev

56

readline专为与终端(即process.stdin.isTTY === true)配合使用而设计。有许多模块可为通用流提供拆分功能,例如split。它使事情变得非常简单:

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

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

6
不,这不对。如果您不想逐行阅读,则根本不需要它
vkurchatkin 2014年

6
提示:如果要在处理所有行之后运行一些代码,请.on('end', doMoreStuff)在第一行之后添加.on()。请记住,如果您只在带有的语句之后正常编写代码.on(),那么该代码将在读取任何输入之前运行,因为JavaScript不同步。
罗里·奥肯

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

为他人共享:

逐行读取流,对于通过管道传输到stdin的大文件来说应该是好的,我的版本:

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

就我而言,程序(链接)返回的行看上去是空的,但实际上具有特殊的终端字符,颜色控制代码和退格键,因此grep其他答案中显示的选项对我不起作用。所以我在Node.js中编写了这个小脚本。我将文件称为tight,但这只是一个随机名称。

#!/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) 
})
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.