交互式地从控制台读取值

我想用一些控制台扩展来制作一个简单的服务器 http 服务器。我找到了要从命令行数据读取的代码片段。

  var i = rl.createInterface(process.stdin, process.stdout, null);
i.question('Write your name: ', function(answer) {
console.log('Nice to meet you> ' + answer);
i.close();
process.stdin.destroy();


});

那么反复提问的问题,我不能简单地使用 while(done) { }循环?同样,如果服务器在提问时间收到输出,那么线路就会被破坏。

216449 次浏览

您不能执行“ while (done)”循环,因为这需要对输入进行阻塞,而 node.js 不喜欢这样做。

相反,设置一个回调函数,每次输入某些内容时调用该函数:

var stdin = process.openStdin();


stdin.addListener("data", function(d) {
// note:  d is an object, and when converted to a string it will
// end with a linefeed.  so we (rather crudely) account for that
// with toString() and then trim()
console.log("you entered: [" +
d.toString().trim() + "]");
});

我使用了另一个 API 来实现这个目的。

var readline = require('readline');
var rl = readline.createInterface(process.stdin, process.stdout);
rl.setPrompt('guess> ');
rl.prompt();
rl.on('line', function(line) {
if (line === "right") rl.close();
rl.prompt();
}).on('close',function(){
process.exit(0);
});

这允许循环提示,直到答案为 right。它还提供了不错的小控制台。你可在此查阅详情@http://nodejs.org/api/readline.html#readline_example_tiny_cli

Readline API 自12’以来发生了很大变化。文档展示了一个从标准流中捕获用户输入的有用例子:

const readline = require('readline');


const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});


rl.question('What do you think of Node.js? ', (answer) => {
console.log('Thank you for your valuable feedback:', answer);
rl.close();
});

更多信息请点击这里。

@ robb 回答在大多数情况下都能正常工作,但是如果输入时间太长,它可能不会像你预期的那样正常工作。

这才是你应该使用的:

const stdin = process.openStdin();
let content = '';


stdin.addListener('data', d => {
content += d.toString();
});


stdin.addListener('end', () => {
console.info(`Input: ${content}`);
});

解释为什么这个解决方案有效:

addListener('data')像缓冲区一样工作,当它满了或者/和它的输入结束时将调用回调。

那么长输入呢?一个单独的 'data'回调函数是不够的,因此您将得到分成两个或更多部分的输入。这通常并不方便。

当标准输入读取器读取完我们的输入后,addListener('end')将通知我们。因为我们已经存储了以前的数据,所以现在可以一起读取和处理它们。

请使用 Readline-sync,这可以让您在没有回调地狱的同步控制台上工作。甚至可以使用密码:

var favFood = read.question('What is your favorite food? ', {
hideEchoBack: true // The typed text on screen is hidden by `*` (default).
});

假设使用 node > = 7.x,我认为这值得使用现代的 async-await答案。

答案仍然使用 ReadLine::question,但是包装它使得 while (done) {}成为可能,这是 OP 明确询问的问题。

var cl = readln.createInterface( process.stdin, process.stdout );
var question = function(q) {
return new Promise( (res, rej) => {
cl.question( q, answer => {
res(answer);
})
});
};

然后举个例子

(async function main() {
var answer;
while ( answer != 'yes' ) {
answer = await question('Are you sure? ');
}
console.log( 'finally you are sure!');
})();

导致接下来的对话

Are you sure? no
Are you sure? no
Are you sure? yes
finally you are sure!

编辑

cl.close();

在剧本的最后。

一个常见的用例可能是应用程序显示一个通用提示并在 switch 语句中处理它。

通过使用在回调中调用自身的 helper 函数,您可以获得与 while 循环等价的行为:

const readline = require('readline');
const rl = readline.createInterface(process.stdin, process.stdout);


function promptInput (prompt, handler)
{
rl.question(prompt, input =>
{
if (handler(input) !== false)
{
promptInput(prompt, handler);
}
else
{
rl.close();
}
});
}


promptInput('app> ', input =>
{
switch (input)
{
case 'my command':
// handle this command
break;
case 'exit':
console.log('Bye!');
return false;
}
});

如果应用程序已经在循环之外的屏幕上打印了内容,那么可以传递一个空字符串而不是 'app> '

我建议使用 《问事报》,因为它提供了一组常见的交互式命令行用户界面。

const inquirer = require('inquirer');


const questions = [{
type: 'input',
name: 'name',
message: "What's your name?",
}];


const answers = await inquirer.prompt(questions);
console.log(answers);

阻塞读取线未阻塞行为

假设您有三个问题需要从控制台回答,因为您现在知道这段代码将不会运行,因为 readline 标准模块具有“解除阻塞”的行为,即每个 rl.questions 是一个独立的线程,所以这段代码将不会运行。

'use strict';


var questionaire=[['First Question: ',''],['Second Question: ',''],['Third Question: ','']];


function askaquestion(question) {
const readline = require('readline');


const rl = readline.createInterface(
{input: process.stdin, output:process.stdout}
);
rl.question(question[0], function(answer) {
console.log(answer);
question[1] = answer;
rl.close();
});
};


var i=0;
for (i=0; i < questionaire.length; i++) {
askaquestion(questionaire[i]);
}


console.log('Results:',questionaire );

运行输出:

node test.js
Third Question: Results: [ [ 'First Question: ', '' ],
[ 'Second Question: ', '' ],
[ 'Third Question: ', '' ] ]        <--- the last question remain unoverwritten and then the final line of the program is shown as the threads were running waiting for answers (see below)
aaa        <--- I responded with a single 'a' that was sweeped by 3 running threads
a        <--- Response of one thread


a        <--- Response of another thread


a        <--- Response of another thread (there is no order on threads exit)

所提出的解决方案使用事件发射器发出解阻塞线程结束的信号,并将循环逻辑和程序结束包含在其侦听器函数中。

'use strict';


var questionaire=[['First Question: ',''],['Second Question: ',''],['Third Question: ','']];


// Introduce EventEmitter object
const EventEmitter = require('events');


class MyEmitter extends EventEmitter {};


const myEmitter = new MyEmitter();
myEmitter.on('continue', () => {
console.log('continue...');
i++; if (i< questionaire.length) askaquestion(questionaire[i],myEmitter);    // add here relevant loop logic
else console.log('end of loop!\nResults:',questionaire );
});
//


function askaquestion(p_question,p_my_Emitter) { // add a parameter to include my_Emitter
const readline = require('readline');


const rl = readline.createInterface(
{input: process.stdin, output:process.stdout}
);
rl.question(p_question[0], function(answer) {
console.log(answer);
p_question[1] = answer;
rl.close();
myEmitter.emit('continue');    // Emit 'continue' event after the question was responded (detect end of unblocking thread)
});
};


/*var i=0;
for (i=0; i < questionaire.length; i++) {
askaquestion(questionaire[i],myEmitter);
}*/


var i=0;
askaquestion(questionaire[0],myEmitter);        // entry point to the blocking loop




// console.log('Results:',questionaire )    <- moved to the truly end of the program

运行输出:

node test2.js
First Question: 1
1
continue...
Second Question: 2
2
continue...
Third Question: 3
3
continue...
done!
Results: [ [ 'First Question: ', '1' ],
[ 'Second Question: ', '2' ],
[ 'Third Question: ', '3' ] ]

这里有一个例子:

const stdin = process.openStdin()


process.stdout.write('Enter name: ')


stdin.addListener('data', text => {
const name = text.toString().trim()
console.log('Your name is: ' + name)


stdin.pause() // stop reading
})

产出:

Enter name: bob
Your name is: bob

我的方法是使用 异步发电机异步发电机

假设你有一系列的问题:

 const questions = [
"How are you today ?",
"What are you working on ?",
"What do you think of async generators ?",
]

为了使用 await关键字,您必须将程序封装到一个异步 IIFE 中。

(async () => {


questions[Symbol.asyncIterator] = async function * () {
const stdin = process.openStdin()


for (const q of this) {
// The promise won't be solved until you type something
const res = await new Promise((resolve, reject) => {
console.log(q)


stdin.addListener('data', data => {
resolve(data.toString())
reject('err')
});
})


yield [q, res];
}


};


for await (const res of questions) {
console.log(res)
}


process.exit(0)
})();

预期成果:

How are you today ?
good
[ 'How are you today ?', 'good\n' ]
What are you working on ?
:)
[ 'What are you working on ?', ':)\n' ]
What do you think about async generators ?
awesome
[ 'What do you think about async generators ?', 'awesome\n' ]

如果你想得到所有问题的答案,你可以通过一个简单的修改来实现:

const questionsAndAnswers = [];


for await (const res of questions) {
// console.log(res)
questionsAndAnswers.push(res)
}


console.log(questionsAndAnswers)


/*
[ [ 'How are you today ?', 'good\n' ],
[ 'What are you working on ?', ':)\n' ],
[ 'What do you think about async generators ?', 'awesome\n' ] ]
*/

我已经为 read 目录创建了一个小脚本,并将控制台名称 new file (例如: ‘ name.txt’)和文本写入 file。

const readline = require('readline');
const fs = require('fs');


const pathFile = fs.readdirSync('.');


const file = readline.createInterface({
input: process.stdin,
output: process.stdout
});


file.question('Insert name of your file? ', (f) => {
console.log('File is: ',f.toString().trim());
try{
file.question('Insert text of your file? ', (d) => {
console.log('Text is: ',d.toString().trim());
try {
if(f != ''){
if (fs.existsSync(f)) {
//file exists
console.log('file exist');
return file.close();
}else{
//save file
fs.writeFile(f, d, (err) => {
if (err) throw err;
console.log('The file has been saved!');
file.close();
});
}
}else{
//file empty
console.log('Not file is created!');
console.log(pathFile);
file.close();
}
} catch(err) {
console.error(err);
file.close();
}
});
}catch(err){
console.log(err);
file.close();
}
});

这太复杂了,简单点说就是:

var rl = require('readline');
rl.createInterface... etc

就是利用

var rl = require('readline-sync');

然后它会等待,当你使用

rl.question('string');

那就更容易重复了。 例如:

var rl = require('readline-sync');
for(let i=0;i<10;i++) {
var ans = rl.question('What\'s your favourite food?');
console.log('I like '+ans+' too!');
}

我必须在 Node 中编写一个“ tic-tac-toe”游戏,它从命令行获取输入,并编写这个基本的异步/等待代码块来完成这个任务。

const readline = require('readline')


const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});


async function getAnswer (prompt) {
const answer = await new Promise((resolve, reject) =>{
rl.question(`${prompt}\n`, (answer) => {
resolve(answer)
});
})
return answer
}


let done = false
const playGame = async () => {
let i = 1
let prompt = `Question #${i}, enter "q" to quit`
while (!done) {
i += 1
const answer = await getAnswer(prompt)
console.log(`${answer}`)
prompt = processAnswer(answer, i)
}
rl.close()
}


const processAnswer = (answer, i) => {
// this will be set depending on the answer
let prompt = `Question #${i}, enter "q" to quit`
// if answer === 'q', then quit
if (answer === 'q') {
console.log('User entered q to quit')
done = true
return
}
// parse answer


// if answer is invalid, return new prompt to reenter


// if answer is valid, process next move


// create next prompt
return prompt
}


playGame()

最简单的方法是使用 readline-sync

它一个接一个地处理输入和输出。

npm i readline-sync

例如:

var firstPrompt = readlineSync.question('Are you sure want to initialize new db? This will drop whole database and create new one, Enter: (yes/no) ');


if (firstPrompt === 'yes') {
console.log('--firstPrompt--', firstPrompt)
startProcess()
} else if (firstPrompt === 'no') {
var secondPrompt = readlineSync.question('Do you want to modify migration?, Enter: (yes/no) ');
console.log('secondPrompt ', secondPrompt)
startAnother()
} else {
console.log('Invalid Input')
process.exit(0)
}
### Building on Rob's answer from 2011
a #CoffeeScript way to do the listener. ###


standin = process.stdin
out = process.stdout


standin.setEncoding('ascii')


# no data values returned inside the function
#  are available outside the function.
#  So running entire program from listener.
standin.addListener('data', (buffer) ->
    

arrayIn = buffer.toString().split('\n')
t = lineIn()
    

console.log("lineIn=" + lineIn() + " line #=" + lineNum
+ " arrayIn=[" + arrayIn + "]")
)