如何在 node.js 中读取整个文本流?

Posted

技术标签:

【中文标题】如何在 node.js 中读取整个文本流?【英文标题】:How to read an entire text stream in node.js? 【发布时间】:2012-11-04 20:18:24 【问题描述】:

在 RingoJS 中有一个名为 read 的 function 允许您读取整个流直到到达末尾。这在您制作命令行应用程序时很有用。例如你可以写一个tacprogram如下:

#!/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 (或 Ctrl+Z(在 Windows 上)向end of transmission 发送信号。

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

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

file descriptor for 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 中将其作为字符串返回?一个代码示例会很有帮助。

【问题讨论】:

您不能在异步流中同步读取。你为什么要这样做? 我正在尝试做同样的事情。原因是在我的程序中创建一个交互式选项,这有很多原因。异步阅读器并没有太大帮助。 这里是npmjs.com/package/readline-sync: ***.com/questions/8452957/… 【参考方案1】:

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

Event: 'data'
Event: 'end'

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

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

【讨论】:

【参考方案2】:

由于 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 解析器)

【讨论】:

你可以在恢复后执行process.stdin.setEncoding('utf-8');,回调中的bug已经是字符串了。 类似,但使用Buffer.concat(): ***.com/questions/10686617/… @Mitar:这是buf,而不是bug 为什么要反转字符串? 这只是用数据做某事的一个例子【参考方案3】:

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

【讨论】:

这个模块允许你用另一个字符串散布块。可能只对调试有用:npmjs.org/package/join-stream【参考方案4】:

让我来说明 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 是一个流。

【讨论】:

【参考方案5】:

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

const chunks = []
for await (let chunk of readable) 
  chunks.push(chunk)

console.log(Buffer.concat(chunks))

【讨论】:

【参考方案6】:

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

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

【讨论】:

以上是关于如何在 node.js 中读取整个文本流?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Node.js 创建带有缓冲区的读取流

如何在 Node JS 中读取文本文件并将其作为 JSON 对象返回?

node 之 fs流读写

Node.js - 如何将流转换为字符串

如何将文件逐行读入node.js中的数组[重复]

如何在 Node.js 流回调中聚合从异步函数生成的 Promise?