如何在Node中逐行读取stdin

Mat*_*son 141 stdin node.js

我正在寻找使用命令行调用处理节点的文本文件,如:

node app.js < input.txt

文件的每一行都需要单独处理,但一旦处理,输入行就会被遗忘.

使用stdin的on-data监听器,我得到一个字节大小的输入蒸汽,所以我设置了它.

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);
});
Run Code Online (Sandbox Code Playgroud)

但这似乎很草率.必须按线阵列的第一个和最后一个项目.有没有更优雅的方式来做到这一点?

lev*_*evi 179

您可以使用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);
})
Run Code Online (Sandbox Code Playgroud)

  • 看起来如果你将参数`terminal:false`传递给createInterface,它就解决了这个问题. (11认同)
  • 这似乎适合在控制台中手动输入输入,但是,当我将文件传递给命令时,文件将被发送到stdout.一个bug?此时读取线被认为是不稳定的. (3认同)
  • 不幸的是,我需要stdout.我把它排除在我的问题之外,但我正在尝试将该应用程序用作`node app.js <input.txt> output.txt`. (3认同)

vku*_*kin 54

readline专门设计用于终端(即process.stdin.isTTY === true).有许多模块为通用流提供分离功能,例如拆分.它让事情变得非常简单:

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

function processLine (line) {
  console.log(line + '!')
}
Run Code Online (Sandbox Code Playgroud)

  • 不,这不对.如果您不想逐行阅读,则根本不需要它 (6认同)
  • 提示:如果要在处理完所有行后运行一些代码,请在第一个`.on()`之后添加`.on('end',doMoreStuff)`.请记住,如果您只是在使用`.on()`的语句之后正常编写代码,那么该代码将在读取任何输入之前运行,因为JavaScript不是同步的. (6认同)

Zea*_*lic 41

// Work on POSIX and Windows
var stdinBuffer = fs.readFileSync(0); // STDIN_FILENO = 0
console.log(stdinBuffer.toString());
Run Code Online (Sandbox Code Playgroud)

  • @AlexChaffee:如果没有stdin输入或stdin已关闭,Windows上似乎存在一个错误(仍然存在v9.10.1) - 请参阅[此GitHub问题](https://github.com/nodejs/node/问题/ 19831).但是,除此之外,解决方案_does_在Windows上运行. (3认同)
  • 你能提供一些细节吗?已经有一个高度评价的接受答案 (2认同)
  • 这对我不起作用(节点v9.2.0,Windows)。错误:EISDIR:对目录的非法操作,位于tryStatSync(fs.js:534:13)的fstat (2认同)
  • 在节点v6.11.2,OSX上为我工作. (2认同)
  • 效果很好,并且是目前为止最短的,可以通过执行`fs.readFileSync(0).toString()使其更短 (2认同)
  • 请注意,“幻数”0 可以替换为更清晰的“process.stdin.fd”(它只是硬编码为 0,但使您在做什么更加明显) (2认同)
  • 我的笔记:`echo hoge | 节点-e 'console.log(require("fs").readFileSync(0).toString())'` (2认同)
  • 很好,不需要外部依赖和设置 npm 包。因此,可以在任何地方以内联模式使用。 (2认同)
  • @Dave但是请注意,使用“process.stdin.fd”可能会导致问题,如下所述:/sf/ask/2825365861/。像`const STDIN_FILE_DESCRIPTOR = 0;这样的东西 fs.readFileSync(STDIN_FILE_DESCRIPTOR);`,在所有情况下都有效,同时保持您的意图明确。 (2认同)

小智 10

#!/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);
Run Code Online (Sandbox Code Playgroud)


Tha*_*hai 8

自从发布接受的答案以来,Node.js 已经发生了很大的变化,所以这里是一个现代示例,用于readline将流拆分为行,for await从流中读取数据,以及ES 模块

import { createInterface } from "node:readline"

for await (const line of createInterface({ input: process.stdin })) {
  // Do something with `line` here.
  console.log(line)
}
Run Code Online (Sandbox Code Playgroud)