อ่านค่าจากคอนโซลแบบโต้ตอบ


155

ฉันคิดว่าจะสร้างเซิร์ฟเวอร์ 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) { }วนซ้ำได้หรือไม่ ยังดีถ้าเซิร์ฟเวอร์ได้รับผลลัพธ์ในเวลาคำถามมันทำลายสาย


5
ฉันถือว่าrlคุณหมายความว่าอ่านแล้วเหรอ?
jpaugh

คุณสามารถใช้ส่วนต่อประสานที่ไม่มีการปิดกั้นเช่นเดียวกับที่ใช้ในคำตอบนี้จากนั้นคุณสามารถwhile(done)วนซ้ำ
Keyvan

คำตอบ:


182

คุณไม่สามารถทำลูป "ในขณะที่ (เสร็จสิ้น)" ได้เนื่องจากจะต้องมีการบล็อกอินพุตสิ่งที่ 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
ขอบคุณงานนี้ผู้ฟัง "จบ" อนุญาตให้เรียกการดำเนินการปิดและพูดว่า 'ลาก่อน' ได้หรือไม่
Risto Novik

ฉันลบผู้ฟัง "จบ" ออกจากตัวอย่างฉันไม่ทราบว่าจะมีประโยชน์ตรงไหน
ปล้น

2
คุณสามารถทำให้เอาต์พุตสตริงเป็น d.toString () ได้ง่ายขึ้น trim ()
MKN Web Solutions

6
คำตอบนี้วันที่ 2011 และมีการเปลี่ยนแปลงมากตั้งแต่ โดยเฉพาะอย่างยิ่งส่วนแรกของคำตอบคุณไม่สามารถทำในขณะที่วง ...ไม่ถืออีกต่อไป ใช่คุณสามารถมีวงในขณะที่และยังไม่ปิดกั้นขอบคุณรูปแบบการรอคอย async คำตอบอื่น ๆ สะท้อนให้เห็นว่า ให้กับทุกคนที่อ่านข้อความนี้ในปัจจุบัน - โปรดปรึกษาคำตอบอื่น ๆ ได้เป็นอย่างดี
Wiktor Zychla

1
หากต้องการติดตาม @WiktorZychla ฟังก์ชัน process.openStdin ในขณะที่ยังใช้งานได้ถูกเลิกใช้ประมาณปี 2011 และคุณจะไม่พบเอกสารใด ๆ ที่เกี่ยวข้อง
calder.ty

111

ฉันใช้ 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นี้จะช่วยให้เพื่อให้ในวงจนคำตอบคือ นอกจากนี้ยังจะช่วยให้มีความสุขเล็ก ๆ น้อย ๆ console.You สามารถค้นหารายละเอียด @ http://nodejs.org/api/readline.html#readline_example_tiny_cli


11
นี่คือคำตอบที่ดี สิ่งที่อาจไม่ชัดเจน (แต่เป็นข้อได้เปรียบใหญ่) คือ readline ไม่ใช่การพึ่งพาจากภายนอก: เป็นส่วนหนึ่งของ node.js
jlh

51

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

ข้อมูลเพิ่มเติมที่นี่


5
นี้เป็นเพียงตัวอย่างพื้นฐาน คุณมีปฏิสัมพันธ์อย่างไร คำถามคำตอบ? มีหลายทางเลือกและไม่ชอบ วิธีเปิด rl ใหม่อีกครั้งเมื่อปิดหากไม่สามารถทำงานกับ open rl เพื่อโต้ตอบกับผู้ใช้รวมถึงตรรกะบางอย่าง
Pawel Cioch

28

ฉันเชื่อว่านี่สมควรasync-awaitคำตอบที่ทันสมัยสมมติว่าโหนด> = 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!

ตรงนี้เป็นคำตอบที่ฉันต้องการ ฉันคิดว่ามันควรจะเป็นอันดับหนึ่ง
William Chou

สวย. จำเป็นต้องรอ Async สำหรับสคริปต์ขนาดใหญ่ นี่คือสิ่งที่ฉันต้องการ
Abhay Shiro

25

โปรดใช้readline-syncซึ่งจะช่วยให้คุณทำงานกับคอนโซลแบบซิงโครนัสโดยไม่มีการเรียกกลับ ใช้งานได้กับรหัสผ่าน:

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


5
สิ่งนี้ต้องการการพึ่งพาเป็นพิเศษดังนั้นฉันจึงต้องการโซลูชันอื่น
Risto Novik

ไม่ทำงานบน SO "Uncaught ReferenceError: read is not
define

12

@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 เสร็จสิ้นการอ่านอินพุตของเรา เนื่องจากเราได้จัดเก็บข้อมูลก่อนหน้านี้เราจึงสามารถอ่านและประมวลผลข้อมูลทั้งหมดเข้าด้วยกันได้


3
เมื่อฉันใช้รหัสด้านบนและใส่ข้อมูลบางส่วนจากนั้นกด "Enter" คอนโซลจะขอข้อมูลเพิ่มเติมจากฉัน เราจะยุติมันได้อย่างไร
Matan Tubul

5

ฉันแนะนำให้ใช้Inquirerเนื่องจากมันมีชุดของส่วนติดต่อผู้ใช้บรรทัดคำสั่งแบบโต้ตอบทั่วไป

const inquirer = require('inquirer');

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

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

5

นี่คือตัวอย่าง:

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

พี่ชายของคำตอบที่ดี !! เรียบง่ายและชัดเจน
MD.JULHAS HOSSAIN

3

นี่คือความซับซ้อน รุ่นที่ง่ายขึ้นของ:

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

2

กรณีการใช้งานทั่วไปอาจเป็นไปได้ที่แอปจะแสดงพรอมต์ทั่วไปและจัดการในคำสั่งเปลี่ยน

คุณสามารถรับพฤติกรรมเทียบเท่ากับ while loop โดยใช้ฟังก์ชันตัวช่วยที่จะเรียกตัวเองในการเรียกกลับ:

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> 'หากแอปของคุณพิมพ์บางสิ่งไปยังหน้าจอนอกวงวนนี้


2

วิธีการของฉันนี้จะใช้async ปั่นไฟ

สมมติว่าคุณมีคำถามมากมาย:

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

ในการใช้awaitคำหลักคุณต้องห่อโปรแกรมของคุณให้เป็น async 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' ] ]
   */

2

ฉันต้องเขียนเกม "tic-tac-toe" ใน Node ที่รับอินพุตจากบรรทัดคำสั่งและเขียน async / คอยบล็อกพื้นฐานของโค้ดที่ทำกลอุบาย

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

1

การปิดกั้นการยกเลิกการบล็อก readline พฤติกรรม

ลองจินตนาการว่าคุณมีสามคำถามที่ต้องตอบจากคอนโซลเนื่องจากคุณรู้ว่าโค้ดนี้จะไม่ทำงานเนื่องจากโมดูลมาตรฐาน 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' ] ]

0

ฉันได้ลองใช้สคริปต์เล็กน้อยเพื่ออ่านไดเรกทอรีและเขียนชื่อไฟล์ใหม่ของคอนโซล (ตัวอย่าง: '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();
  }
});

0

วิธีที่ง่ายที่สุดคือใช้ 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)
}

คุณควรรวมrequireคำสั่งของคุณ ไม่มีเหตุผลที่จะทิ้งมันไว้
solidstatejake
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.