在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
这允许您启动外壳并运行tac
命令。然后,您可以根据需要输入任意数量的行,完成后,您可以按Ctrl +D(或Windows上的Ctrl+Z)来表示传输结束。
我想在node中做同样的事情.js但我找不到任何可以这样做的功能。我想使用fs
库中的readSync
函数进行模拟,如下所示,但无济于事:
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 中的字符串返回?代码示例将非常有帮助。
由于节点.js是面向事件和流的,没有 API 可以等到 stdin 和缓冲区结果结束,但手动操作很容易
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 解析器)
关键是使用这两个 Stream 事件:
Event: 'data'
Event: 'end'
对于stream.on('data', ...)
,您应该将数据数据收集到缓冲区(如果是二进制)或字符串中。
对于on('end', ...)
,您应该使用已完成的缓冲区调用回调,或者是否可以内联它并使用 Promise 库使用 return。
让我来说明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
是一条溪流。
该特定任务有一个模块,称为concat-stream。
如果您处于async
上下文中并且拥有最新版本的 Node.js,这里有一个快速建议:
const chunks = []
for await (let chunk of readable) {
chunks.push(chunk)
}
console.log(Buffer.concat(chunks))
这是一个古老的问题,但值得一提的是,Node.js有一些新的流助手,其中一个是toArray:
require('http')
.createServer(async (req, res) => {
const str = (await req.toArray()).toString().toUpperCase();
res.end(str);
})
.listen(4000);
请注意:此 API 目前标记为实验性,因此可能更适合测试/非生产代码。
在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
。