如何在nodejs中获得同步readline,或使用异步“模拟”它?
Posted
技术标签:
【中文标题】如何在nodejs中获得同步readline,或使用异步“模拟”它?【英文标题】:How to get synchronous readline, or "simulate" it using async, in nodejs? 【发布时间】:2017-09-24 02:05:43 【问题描述】:我想知道是否有一种简单的方法来获得“同步”读取线或至少在 node.js 中获得同步 I/O 的外观
我用这样的东西,但是很尴尬
var readline = require('readline');
var rl = readline.createInterface(
input: process.stdin,
output: process.stdout,
terminal: false
);
var i = 0;
var s1 = '';
var s2 = '';
rl.on('line', function(line)
if(i==0) s1 = line;
else if(i==1) s2 = line;
i++;
)
rl.on('close', function()
//do something with lines
)'
如果它像类似的东西一样简单,我更愿意这样做
var s1 = getline(); // or "await getline()?"
var s2 = getline(); // or "await getline()?"
有用的条件:
(a) 不喜欢使用外部模块或 /dev/stdio 文件句柄,我正在向代码提交网站提交代码,但这些在那里不起作用
(b) 可以使用 async/await 或生成器
(c) 应该是基于行的
(d) 处理前不需要将整个标准输入读入内存
【问题讨论】:
node.js: readSync from stdin?的可能重复 @supersam654 我添加了一些额外的条件来重复数据删除。我的条件是允许使用 getline 类型函数读取(单)行,使用 readline 接口以干净的“同步”方式或使用“async/await”函数来模拟。 【参考方案1】:我们可以一起使用promise和process.stdin事件来模拟一个同步输入系统
const EOL = require("os");
const getLine = async () =>
(
await new Promise((resolve) =>
process.stdin.on("data", (line) =>
resolve("" + line);
);
)
).split(EOL)[0];
const line = await getLine();
console.log(line);
【讨论】:
【参考方案2】:文档中提供了最简单(也是首选)的选项。 https://nodejs.org/api/readline.html#rlquestionquery-options-callback
const util = require('util');
const question = util.promisify(rl.question).bind(rl);
async function questionExample()
try
const answer = await question('What is you favorite food? ');
console.log(`Oh, so your favorite food is $answer`);
catch (err)
console.error('Question rejected', err);
questionExample();
【讨论】:
感谢反馈,但我是在读取数据文件,而不是提示用户。【参考方案3】:以防万一将来有人在这里偶然发现
Node11.7使用async await
添加了对此doc_link的支持
const readline = require('readline');
//const fileStream = fs.createReadStream('input.txt');
const rl = readline.createInterface(
input: process.stdin, //or fileStream
output: process.stdout
);
for await (const line of rl)
console.log(line)
记得用async function()
包裹它,否则你会得到一个reserved_keyword_error
const start = async () =>
for await (const line of rl)
console.log(line)
start()
要读取单个行,您可以手动使用 async
迭代器
const it = rl[Symbol.asyncIterator]();
const line1 = await it.next();
【讨论】:
我可以调用 get a single line 使用这种方法吗?例如,我在循环之前想读一两行,所以我想说像 const header = await rl.next() 但这即使在节点 11.7 中也不起作用 ***.com/questions/45556535/…@ColinD 我发现这些答案的句法复杂性是不必要的复杂。我知道这是完全可行的,但我正在寻找像var h1 = await rl.next(); var h2 = await rl.next(); for await (const line of rl) /* process rest of file */
这样简单的东西,但正如我上面提到的,rl 迭代器似乎没有提供这种功能
哦!我明白了,vl 检查 readline 和 for await
的实现
@AishwatSingh 当我使用你的代码时,我在等待时遇到语法错误。意外的令牌,预期的(你能验证它是否还在工作吗?【参考方案4】:
你可以把它包装在一个承诺中 -
const answer = await new Promise(resolve =>
rl.question("What is your name? ", resolve)
)
console.log(answer)
【讨论】:
【参考方案5】:我想这就是你想要的:
const readline = require('readline');
const rl = readline.createInterface( input: process.stdin , output: process.stdout );
const getLine = (function ()
const getLineGen = (async function* ()
for await (const line of rl)
yield line;
)();
return async () => ((await getLineGen.next()).value);
)();
const main = async () =>
let a = Number(await getLine());
let b = Number(await getLine());
console.log(a+b);
process.exit(0);
;
main();
注意:此答案使用实验性功能,需要 Node v11.7
【讨论】:
这看起来不错,例如,在添加这两个数字之后,您将如何设置一个循环以读取直到输入结束? getLine 如果到达 EOF 将返回 undefined,所以 while (1) let x = await getLine(); if (x === undefined) 中断; /* 然后使用 x */ 应该可以工作。【参考方案6】:与readline
模块一样,还有一个名为readline-sync
的模块,它接受同步输入。
示例:
const reader = require("readline-sync"); //npm install readline-sync
let username = reader.question("Username: ");
const password = reader.question("Password: ", hideEchoBack: true );
if (username == "admin" && password == "foobar")
console.log("Welcome!")
【讨论】:
这应该是这个问题的正确答案(这是最佳实践) @johannchopin 不,这不是我想要的方法。我通过标准输入或大型数据集使用管道,而不是问答,这是该模块的作用,即 readline-sync 的设计目的 还有一个叫readline-async
,以防有人需要承诺。
@johannchopin 这不是最佳实践。 readline-sync 不是标准的。这是一个依赖..【参考方案7】:
试试这个。它仍然不是同步行读取功能的完美复制——例如async
函数稍后仍会发生,因此您的某些调用代码可能会乱序执行,并且您无法从正常的 for
循环中调用它——但它比典型的 .on
更容易阅读或.question
代码。
// standard 'readline' boilerplate
const readline = require('readline');
const readlineInterface = readline.createInterface(
input: process.stdin,
output: process.stdout
);
// new function that promises to ask a question and
// resolve to its answer
function ask(questionText)
return new Promise((resolve, reject) =>
readlineInterface.question(questionText, (input) => resolve(input) );
);
// launch your program since `await` only works inside `async` functions
start()
// use promise-based `ask` function to ask several questions
// in a row and assign each answer to a variable
async function start()
console.log()
let name = await ask("what is your name? ")
let quest = await ask("what is your quest? ")
let color = await ask("what is your favorite color? ")
console.log("Hello " + name + "! " +
"Good luck with " + quest +
"and here is a " + color + " flower for you.");
process.exit()
更新:https://www.npmjs.com/package/readline-promise 实现了它(源代码:https://github.com/bhoriuchi/readline-promise/blob/master/src/index.js#L192)。它还实现了其他几个功能,但它们看起来也很有用,而且没有过度设计,不像其他一些声称做同样事情的 NPM 包。不幸的是,由于https://github.com/bhoriuchi/readline-promise/issues/5,我无法让它工作,但我喜欢它对中心功能的实现:
function ask(questionText)
return new Promise((resolve, reject) =>
readlineInterface.question(questionText, resolve);
);
【讨论】:
我喜欢这个解决方案。使用 Promise 可以利用 async/await 上下文的强大功能,而无需其他库。我宁愿把readline接口作为参数传递给ask函数,这样你就可以使用不同的接口了。【参考方案8】:使用生成器,您的示例将如下所示:
var readline = require('readline');
var rl = readline.createInterface(
input: process.stdin,
output: process.stdout,
terminal: false
);
var i = 0;
var s1 = '';
var s2 = '';
var iter=(function* ()
s1 = yield;
i++;
s2 = yield;
i++;
while (true)
yield;
i++;
)(); iter.next();
rl.on('line', line=>iter.next(line))
rl.on('close', function()
//do something with lines
)
所以yield
在这里的作用就好像它是一个阻塞getline()
,你可以以通常的顺序方式处理行。
UPD: async/await 版本可能如下所示:
var readline = require('readline');
var rl = readline.createInterface(
input: process.stdin,
output: process.stdout,
terminal: false
);
var i = 0;
var s1 = '';
var s2 = '';
var continuation;
var getline = (() =>
var thenable =
then: resolve =>
continuation = resolve;
;
return ()=>thenable;
)();
(async function()
s1 = await getline();
i++;
s2 = await getline();
i++;
while (true)
await getline();
i++;
)();
rl.on('line', line=>continuation(line))
rl.on('close', function()
//do something with lines
)
在这两个“同步”版本中,i
不用于区分行,仅用于计算行的总数。
【讨论】:
【参考方案9】:这是一个示例,但它需要在给出结果之前读取整个标准输入,但这并不理想
var rl = readline.createInterface(
input: process.stdin,
output: process.stdout,
terminal: false
);
function lineiterator()
var currLine = 0;
var lines = [];
return new Promise(function(resolve, reject)
rl.on('line', function (line)
lines.push(line)
)
rl.on('close', function ()
resolve(
next: function()
return currLine < lines.length ? lines[currLine++]: null;
);
)
)
例子
lineiterator().then(function(x)
console.log(x.next())
console.log(x.next())
)
$ echo test$\ntest | node test.js
test
test
【讨论】:
【参考方案10】:因为我不知道你需要多少个字符串,所以我把它们都放在一个数组中
如果您需要更详细的答案或我的答案不准确,请随时发表评论:
var readline = require('readline');
var rl = readline.createInterface(
input: process.stdin,
output: process.stdout,
terminal: false
);
var i = 0;
var strings = [];
rl.on('line', function(line)
// 2 lines below are in case you want to stop the interface after 10 lines
// if (i == 9)
// rl.close()
strings[i] = line
i++
).on('close', function()
console.log(strings)
)
// this is in case you want to stop the program when you type ctrl + C
process.on('SIGINT', function()
rl.close()
)
【讨论】:
这需要将整个标准输入读入内存,我不希望这样,因为我通过标准输入读取大文件并且需要流式传输 它是一个流,所以它进入内存的唯一原因是因为我把它放在变量字符串中,但是如果你用它做其他事情它不会进入内存 我明白了。但我想要类似 perlmy $line = <>
的东西。或 C++ 中的 getline。有这么难吗?我觉得我的请求并没有那么古怪,我想象它是通过生成器或 async/await 完成的
真正的问题是你想对所有的行做什么?
目前数据行是通过标准输入管道进入程序的,数据一般有一些标题行,然后是很多数据。标头没有信号,您只是根据问题需要推断它,并且数据和标头包含字符串和数字的混合。因此,控制 getline 函数比在 on('line') 回调中调整代码要容易得多。以上是关于如何在nodejs中获得同步readline,或使用异步“模拟”它?的主要内容,如果未能解决你的问题,请参考以下文章