web-dev-qa-db-fra.com

Lecture de la valeur de la console, de manière interactive

J'ai pensé faire un serveur HTTP simple avec une extension de console. J'ai trouvé l'extrait à lire à partir de données en ligne de commande.

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

  });

bien pour poser les questions à plusieurs reprises, je ne peux pas simplement utiliser la boucle while(done) { }? De même, si le serveur reçoit une sortie à l'heure des questions, la ligne est ruinée.

140
Risto Novik

vous ne pouvez pas faire une boucle "while (done)" car cela nécessiterait un blocage sur une entrée, quelque chose que node.js n'aime pas faire.

À la place, configurez un rappel à appeler chaque fois que vous entrez quelque chose:

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() + "]");
  });
162
rob

J'ai utilisé une autre API à cet effet.

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

Cela permet d’inviter en boucle jusqu’à ce que la réponse soit right. En outre, cela donne Nice petite console. Vous pouvez trouver les détails @ http://nodejs.org/api/readline.html#readline_example_tiny_cli

101
Madhan Ganesh

L'API Readline a beaucoup changé depuis 12 '. Les doc montrent un exemple utile pour capturer l'entrée de l'utilisateur à partir d'un flux standard:

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

Plus d'informations ici.

43
Patrick.SE

S'il vous plaît utilisez readline-sync , cela vous permet de travailler avec une console synchrone sans appeler les enfers. Même fonctionne avec des mots de passe:

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

Je crois que cela mérite une async-await réponse moderne, en supposant que le nœud> = 7.x est utilisé.

La réponse utilise toujours ReadLine::question mais l'encapsule de sorte que la while (done) {} soit possible, ce que le gestionnaire technique demande explicitement.

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

puis un exemple d'utilisation

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

conduit à la conversation suivante

Are you sure? no
Are you sure? no
Are you sure? yes
finally you are sure!
15
Wiktor Zychla

@rob answer fonctionnera la plupart du temps, mais il se peut que cela ne fonctionne pas comme prévu avec de longues entrées.

C'est ce que vous devriez utiliser à la place:

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

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

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

Explication du pourquoi de cette solution:

addListener('data') fonctionne comme un tampon, le rappel est appelé quand il est plein ou/et que c'est la fin de l'entrée.

Qu'en est-il des entrées longues? Un seul rappel 'data' ne suffira pas, ce qui vous permettra de scinder votre entrée en deux parties ou plus. Ce n'est souvent pas pratique.

addListener('end') nous préviendra lorsque le lecteur stdin aura fini de lire notre entrée. Comme nous avons stocké les données précédentes, nous pouvons maintenant les lire et les traiter toutes ensemble.

12
zurfyx

Je recommande d'utiliser Inquirer , car il fournit un ensemble d'interfaces utilisateur interactives en ligne de commande communes.

const inquirer = require('inquirer');

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

const answers = await inquirer.Prompt(questions);
console.log(answers);
4
Diogo Cardoso

C'est trop compliqué. Une version plus simple de:

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

serait d'utiliser

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

alors il attendra quand vous utiliserez

rl.question('string');

alors il est plus facile de répéter. par exemple:

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!');
}
3
Ragnarok Ragdoll

Un cas d'utilisation courant serait probablement que l'application affiche une invite générique et la gère dans une instruction switch.

Vous pouvez obtenir un comportement équivalent à une boucle while en utilisant une fonction d'assistance qui s'appelle elle-même dans le rappel:

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

Vous pouvez transmettre une chaîne vide au lieu de 'app> ' si votre application imprime déjà quelque chose sur l'écran en dehors de cette boucle.

2
zoran404

Voici un exemple:

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

Sortie:

Enter name: bob
Your name is: bob
2
Miguel Mota

Mon approche serait d'utiliser générateurs asynchrones.

En supposant que vous ayez un tableau de questions:

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

Pour utiliser le mot clé await, vous devez envelopper votre programme dans un fichier IIFE asynchrone.

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

Résultats attendus:

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

Si vous voulez obtenir des questions et des réponses, vous pouvez le faire en modifiant simplement:

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' ] ]
   */
1
Andrei Gătej

Blocage du comportement non bloqué en lecture/Solution alternative avec promesses

Imaginez que vous ayez à répondre à trois questions à partir de la console, car vous savez maintenant que ce code ne fonctionnera pas car le module standard readline a un comportement "débloqué", ce qui signifie que chaque rl.question est un thread indépendant pour que ce code ne s'exécute pas.

'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 );

Sortie en cours:

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

* La solution proposée utilise Promises, donc seulement lorsque nous avons la réponse à la première question, nous traitons la seconde et ainsi de suite.

'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);

Sortie en cours:

>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' } ] ]
1
vlc33

Blocage du comportement readline non bloqué

Imaginez que vous ayez à répondre à trois questions à partir de la console, car vous savez maintenant que ce code ne fonctionnera pas car le module standard readline a un comportement "débloqué", ce qui signifie que chaque rl.question est un thread indépendant pour que ce code ne s'exécute pas.

'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 );

Sortie en cours:

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)

La solution proposée utilise un émetteur d'événements pour signaler la fin d'un thread de déblocage et inclut la logique de la boucle et la fin du programme dans la fonction d'écoute.

'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

Sortie en cours:

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

J'ai créé un petit script pour le répertoire de lecture et écrit un nouveau nom de console (exemple: 'name.txt') et du texte dans le fichier.

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

Je devais écrire un jeu "tic-tac-toe" dans Node prenant les entrées à partir de la ligne de commande et écrivant ce bloc de code asynchrone/wait de base qui faisait l'affaire.

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()
0
Stefan Musarra