前座:既存の手法(非同期 or 入力全行)
主に競技プログラミングなどのシーンで標準入力からデータが受け渡されることは多いです。競技でなくとも、同様の形式でコーディングテストが行われることもあります。
こういったときに Node.js だと少し煩雑な手順を取る必要があります。
readline
const rl = require("readline").createInterface({
input: process.stdin,
output: process.stdout,
});
const lines = [];
rl.on("line", (line) => {
lines.push(line);
// ... 多分入力処理
});
rl.on("close", () => {
// 入力が閉じたのでデータは受け取った
// データを全て受け取った前提の処理がここ
});
何も知らない人から見ると呪文です。
上から順に実行されるのはそうなのですが、rl.on
では「イベントに対するコールバックを設定」という処理が行われるだけなので rl.on("line", ...);
の直後に処理を書いたとしてもデータが入っているとは限らないのが罠です。
インタラクティブな場面では気持ちがいいかもしれません。
fs.readFileSync
const fs = require("fs");
const wholeInput = fs.readFileSync("/dev/stdin", "utf8");
const inputLines = wholeInput.split("\n");
const n = Number(inputLines[0]);
// ...
こちらは比較的まともです。標準入力からの入力を全て文字列として受け取るものです。
強いて欠点を挙げるとすれば以下のようになるでしょう。
- インタラクティブな場面には向かない
- 行を取るには
split
してインデックスを指定する必要がある
作ってみた
正直上の二つがあれば特に困らないのですが、例えば ruby や python などで簡単に利用できる gets
や input()
などのような関数が Node.js にもあればなあ、と思い作ってみました。
使う
ローカルにインストールして利用する場合は npm
で直接 github 上のリポジトリを指定してください。
(10/28 追記)npm に publish しました。
https://www.npmjs.com/package/node-gets
$ npm install --save node-gets
GitHub にあるリポジトリを指定してインストールすることもできます(<owner>/<repository>
と書けば GitHub からインストールできるらしい)。
$ npm install --save cwd-k2/node-gets
(10/28 追記終わり)
以下のような書き心地になります。
const gets = require("node-gets").createGets();
const n = Number(gets().trim());
const a = gets().trim().split(" ").map(Number);
// ...
- 行入力末尾の改行文字などは付いたまま
- EOF 的な感じで入力がなくなると
undefined
を返すようになる
問題点
- テストがない
- ベンチマークがない
- 実装上の問題
一行読むたびに内部のバッファでコピーが発生している(リングバッファにすべきなど色々あると思います)-
\n
以外の文字での分割に対応していない
最後に
他に問題点があったり、直してやったぞということがあれば issue や PR をお願いします。