25

在 RingoJS 中有一个函数调用read它允许你读取整个流直到到达末尾。这在您制作命令行应用程序时很有用。例如,您可以编写如下tac 程序:

#!/usr/bin/env ringo

var string = system.stdin.read(); // read the entire input stream
var lines = string.split("\n");   // split the lines

lines.reverse();                  // reverse the lines

var reversed = lines.join("\n");  // join the reversed lines
system.stdout.write(reversed);    // write the reversed lines

这允许您启动 shell 并运行tac命令。然后,您可以根据需要输入任意数量的行,完成后您可以按Ctrl+ D(或Windows 上的 +)表示Ctrl传输结束Z

我想在 node.js 中做同样的事情,但我找不到任何可以这样做的函数。我想使用库中的readSync 函数fs模拟如下,但无济于事:

fs.readSync(0, buffer, 0, buffer.length, null);

stdin的文件描述符(第一个参数)是0. 所以它应该从键盘读取数据。相反,它给了我以下错误:

Error: ESPIPE, invalid seek
    at Object.fs.readSync (fs.js:381:19)
    at repl:1:4
    at REPLServer.self.eval (repl.js:109:21)
    at rli.on.self.bufferedCmd (repl.js:258:20)
    at REPLServer.self.eval (repl.js:116:5)
    at Interface.<anonymous> (repl.js:248:12)
    at Interface.EventEmitter.emit (events.js:96:17)
    at Interface._onLine (readline.js:200:10)
    at Interface._line (readline.js:518:8)
    at Interface._ttyWrite (readline.js:736:14)

您将如何同步收集输入文本流中的所有数据并将其作为字符串在 node.js 中返回?一个代码示例将非常有帮助。

4

6 回答 6

32

由于 node.js 是面向事件和流的,因此没有 API 可以等到标准输入和缓冲区结果结束,但手动操作很容易

var content = '';
process.stdin.resume();
process.stdin.on('data', function(buf) { content += buf.toString(); });
process.stdin.on('end', function() {
    // your code here
    console.log(content.split('').reverse().join(''));
});

在大多数情况下,最好不要缓冲数据并在传入块到达时对其进行处理(使用已经可用的流解析器链,如 xml 或 zlib 或您自己的 FSM 解析器)

于 2012-11-16T05:53:57.753 回答
15

关键是要使用这两个 Stream 事件:

Event: 'data'
Event: 'end'

因为stream.on('data', ...)您应该将数据数据收集到缓冲区(如果它是二进制文件)或字符串中。

因为on('end', ...)您应该使用完成的缓冲区调用回调,或者如果您可以内联它并使用 Promises 库使用 return。

于 2012-11-16T05:25:46.520 回答
6

让我来说明 StreetStrider 的答案。

这是使用concat-stream 的方法

var concat = require('concat-stream');

yourStream.pipe(concat(function(buf){
    // buf is a Node Buffer instance which contains the entire data in stream
    // if your stream sends textual data, use buf.toString() to get entire stream as string
    var streamContent = buf.toString();
    doSomething(streamContent);
}));

// error handling is still on stream
yourStream.on('error',function(err){
   console.error(err);
});

请注意,这process.stdin是一个流。

于 2016-03-13T16:12:49.670 回答
5

该特定任务有一个模块,称为concat-stream

于 2014-05-05T18:46:09.300 回答
1

如果您在async上下文中并且拥有最新版本的 Node.js,这里有一个快速建议

const chunks = []
for await (let chunk of readable) {
  chunks.push(chunk)
}
console.log(Buffer.concat(chunks))
于 2021-09-01T11:33:02.190 回答
0

在 Windows 上,我在此处发布的其他解决方案中遇到了一些问题——当没有输入时,程序将无限期地运行。

这是现代 NodeJS 的 TypeScript 实现,使用异步生成器for await- 比使用旧的基于回调的 API 更简单、更健壮,这适用于 Windows:

import process from "process";

/**
 * Read everything from standard input and return a string.
 * 
 * (If there is no data available, the Promise is rejected.)
 */
export async function readInput(): Promise<string> {  
  const { stdin } = process;

  const chunks: Uint8Array[] = [];

  if (stdin.isTTY) {
    throw new Error("No input available");
  }

  for await (const chunk of stdin) {
    chunks.push(chunk);
  }

  return Buffer.concat(chunks).toString('utf8');
}

例子:

(async () => {
  const input = await readInput();

  console.log(input);
})();

try/catch(如果您想处理 Promise 拒绝并在没有输入时显示更用户友好的错误消息,请考虑添加一个。)

于 2021-09-15T12:54:13.170 回答