Чтение значения с консоли в интерактивном режиме

Я думал создать простой сервер 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) { }? Также хорошо, если сервер получает выходные данные во время запроса, он разрушает строку.

Ответ 1

вы не можете сделать цикл 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() + "]");
  });

Ответ 2

Я использовал для этого другой 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

Ответ 3

API Readline изменился совсем немного с 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();
});

Дополнительная информация здесь

Ответ 4

Используйте readline-sync, это позволяет работать с синхронной консолью без обратных вызовов. Даже работает с паролями:

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

Ответ 5

Я считаю, что это заслуживает современного ответа async-await, предполагая, что используется node >= 7.x.

Ответ по-прежнему использует 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!

Ответ 6

Ответ на

@rob будет работать большую часть времени, но он может не работать так, как вы ожидаете, с большими вводами.

Вот что вы должны использовать вместо этого:

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') уведомит нас, когда читатель stdin будет прочитан наш ввод. Поскольку мы сохраняем предыдущие данные, мы теперь можем читать и обрабатывать все это вместе.

Ответ 7

Я рекомендую использовать Inquirer, поскольку он предоставляет набор общих интерактивных пользовательских интерфейсов командной строки.

const inquirer = require('inquirer');

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

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

Ответ 8

Это слишком сложно. Более простая версия:

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\ your favourite food?');
    console.log('I like '+ans+' too!');
}

Ответ 9

Распространенным вариантом использования, вероятно, будет приложение для отображения общего приглашения и обработки его в операторе switch.

Вы можете получить поведение, эквивалентное циклу 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> ' если ваше приложение уже что-то печатает на экране за пределами этого цикла.

Ответ 10

Вот пример:

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

Ответ 11

Блокировка чтения строки разблокированного поведения

Представьте, что вам нужно ответить на три вопроса из консоли, поскольку теперь вы знаете, что этот код не будет выполняться, поскольку стандартный модуль readline имеет "разблокированное" поведение, в котором каждый rl.question является независимым потоком, поэтому этот код не будет выполняться.

'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' ] ]

Ответ 12

Блокировка чтения строк разблокированного поведения/Альтернативное решение с обещаниями

Представьте, что у вас есть три вопроса, на которые нужно ответить из консоли, поскольку теперь вы знаете, что этот код не будет работать, поскольку стандартный модуль readline имеет "разблокированное" поведение, в котором говорится, что каждый rl.question является независимым потоком, поэтому этот код не будет выполняться.

'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: ',null],['Second Question: ',null],['Third Question: ',null]];

function askaquestion(p_questionaire,p_i) {
p_questionaire[p_i][1] = new Promise(function (resolve,reject){ 
const readline = require('readline');

const rl = readline.createInterface(
    {input: process.stdin, output:process.stdout}
    );
rl.question(p_questionaire[p_i][0], function(answer) {
    //console.log(answer);
    resolve(answer),reject('error');
    rl.close();
    });
});
p_questionaire[p_i][1].then(resolve=>{
    if (p_i<p_questionaire.length-1) askaquestion(p_questionaire,p_i+1);
    else console.log('Results: ',p_questionaire) });
};

askaquestion(questionaire,0);

Запуск выхода:

>node test3.js
First Question: 1
Second Question: 2
Third Question: 3
Results:  [ [ 'First Question: ', Promise { '1' } ],
  [ 'Second Question: ', Promise { '2' } ],
  [ 'Third Question: ', Promise { '3' } ] ]

Ответ 13

Мой подход к этому будет использовать асинхронные генераторы.

Если у вас есть множество вопросов:

 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' ] ]
   */

Ответ 14

Я создал небольшой скрипт для чтения каталога и записи нового имени консоли (например, "name.txt") и текста в файл.

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

Ответ 15

Я должен был написать игру "крестики-нолики" в Node, которая брала данные из командной строки, и написал этот базовый блок кода async/await, который сделал свое дело.

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