Nodejs 按行读取控制台输入(stdin)的几种方法

Author Avatar
Equim 2017年2月21日
  • 在其它设备中阅读本文章
Read: 3 minWords: 607Last Updated: 17-12-25Written in: MarkdownLicense: CC-BY-NC-4.0

在用 Nodejs 写 CLI 应用或者做OJ的时候经常会遇到要从 stdin 读取输入的情况,这里总结了几种不依赖第三方库来按行读取输入的方法。

输出一行处理一次的情形

优点:简洁方便,平台无关。可用于编写 REPL 应用。
缺点:某些 OJ 的 node 版本可能不支持readline,对此请参考OJ专用的方法。

require('readline').createInterface({
    input: process.stdin,
    output: process.stdout
}).on('line', function (line) {
    // TODO: 处理这一行输入
    // ...
});

注:可以参阅 Readline 的 API 文档

输入多行处理一次的情形

优点:同上。
缺点:同上。

var lineCount = 0;
var groupBuffer = [];
const rl = require('readline').createInterface({
    input: process.stdin,
    output: process.stdout
});

rl.on('line', function (line) {
    groupBuffer.push(line.trim());

    // 假设把五行作为一组输入
    if (++lineCount % 5 === 0) {
        // TODO: 处理这五行输入
        // ...
        groupBuffer = [];
    }

    // 假设超过17行时不再处理stdin
    if (lineCount >= 17) {
        rl.close();
    }
});


需要互动问答的情形

优点:便于引导用户正确输入。
缺点:要用到 Promise,async/await 等特性,版本兼容性不太好,也比较复杂。

const rl = require('readline').createInterface({
    input: process.stdin,
    output: process.stdout
});
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const question = (query) => new Promise(resolve => rl.question(query, (answer) => resolve(answer)));

(async () => {
    let answer = await question('这个是异步的还是同步的? ');
    console.log(`真的是${answer}吗QAQ……`);

    answer = await question('async/await好不好用啊 ');
    console.log(`${answer}……`);

    answer = await question('等待多少秒? ');
    rl.pause();
    await wait(parseInt(answer) * 1000);
    rl.resume();

    while ((await question('输入bye退出 ')).trim() !== 'bye');

    console.log('Good day.');

    rl.close();
})();

注:上例中的waitquestion函数如果展开的话是这样的

function wait(ms) {
    return new Promise(function (resolve) {
        return setTimeout(resolve, ms);
    });
}
function question(query) {
    return new Promise(function (resolve) {
        return rl.question(query, function (answer) {
            return resolve(answer);
        });
    });
}


OJ 专用

优点:不需要readline,直接用fs实现,版本兼容性好,对OJ非常友好。
缺点:只适用于类 Unix 系统,而且只能在 stdin 被重定向的时候才能使用,无法互动输入(实际上是读取已经事先写好然后被重定向到 stdin 的输入)。

var raw = require('fs').readFileSync('/dev/stdin', 'utf8').trim().split('\n');
var line;
while (line = raw.shift()) {
    // TODO: 处理单行数据
    // ...
}


另一种 OJ 专用

优点:对 OJ 非常友好。
缺点:似乎对 Windows 不怎么友好。

var raw = '';
process.stdin.on('readable', function () {
    var chunk = process.stdin.read();
    if (chunk) {
        raw += chunk.toString();
    }
});
process.stdin.on('end', function () {
    var lines = raw.trim().split('\n');
    // TODO: 处理数据
});

注:对于 Node.js v0.10.x 之前的版本,还需要在前面一行process.stdin.resume()

知识共享许可协议
本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。

本文链接:https://ekyu.moe/article/ways-to-readline-in-nodejs/