Node.jsを使ったコードの練習ということで。
お題として、簡単なgrepコマンドっぽいものを書いてみます。こういう動作設定で書いてみましょう。
まあ、ありきたりな感じですね。
これを前提として、作ってみましょう。対象のNode.jsのバージョンは、こちらとします。
$ node -v
v8.5.0
まずは正攻法的な感じでいくと、File SystemのreadFileを使うのが正しいでしょうか?
書いてみたのは、こちら。
grep.js
const fs = require("fs"); const args = process.argv.slice(2); const filePath = args[0]; const pattern = args[1]; fs.readFile(filePath, "utf8", (err, data) => { if (err) throw err; const lines = data.split(/\r?\n/); const size = lines.length; const maxWidth = size.toString().length; lines .map((line, index) => [(index + 1).toString().padStart(maxWidth, " "), line]) .filter(lineWithIndex => lineWithIndex[1].search(pattern) > -1) .forEach(lineWithIndex => { console.log(`${lineWithIndex[0]}: ${lineWithIndex[1]}`); }); });
FileSystem#readLineだと、ファイルの中身が全部渡ってくるようなので、ここではsplitしてから配列内をパターンにマッチするかどうかテストすることにしました。
const lines = data.split(/\r?\n/); const size = lines.length; const maxWidth = size.toString().length; lines .map((line, index) => [(index + 1).toString().padStart(maxWidth, " "), line]) .filter(lineWithIndex => lineWithIndex[1].search(pattern) > -1) .forEach(lineWithIndex => { console.log(`${lineWithIndex[0]}: ${lineWithIndex[1]}`); });
データの全量がわかっているので、行番号も付けてみました。
試してみましょう。
$ node grep.js grep.js con.+ 1: const fs = require("fs"); 3: const args = process.argv.slice(2); 4: const filePath = args[0]; 5: const pattern = args[1]; 10: const lines = data.split(/\r?\n/); 11: const size = lines.length; 12: const maxWidth = size.toString().length; 18: console.log(`${lineWithIndex[0]}: ${lineWithIndex[1]}`);
OKですね。
とはいえ、この方法だとファイルの中身を一気に全部読んでしまう(?)というか、行単位には扱えないので、このあたりが
どうにかならないかな?と思ってちょっと調べたらReadlineを使うとできそうな感じです。
Readline | Node.js v10.9.0 Documentation
Readlineを使った例は、こちら。
readline-grep.js
const fs = require("fs"); const readline = require("readline"); const args = process.argv.slice(2); const filePath = args[0]; const pattern = args[1]; const readStream = fs.createReadStream(filePath, { encoding: "utf8" }); const rl = readline.createInterface({ input: readStream }); let lineIndex = 0; rl.on("line", line => { lineIndex++; if (line.search(pattern) > -1) { console.log(`${lineIndex}: ${line}`); } });
ReadlineにはStreamを渡す必要があるみたいなので、FileSystemのcreateReadStreamでReadable Streamを作成します。
fs.createReadStream
こちらをReadlineのcreateInterfaceの入力として設定します、と。
readline.createInterface
この例では、特に行番号はフォーマットしません。
試してみましょう。
$ node readline-grep.js grep.js con.+ 1: const fs = require("fs"); 3: const args = process.argv.slice(2); 4: const filePath = args[0]; 5: const pattern = args[1]; 10: const lines = data.split(/\r?\n/); 11: const size = lines.length; 12: const maxWidth = size.toString().length; 18: console.log(`${lineWithIndex[0]}: ${lineWithIndex[1]}`); $ node readline-grep.js readline-grep.js con.+ 1: const fs = require("fs"); 2: const readline = require("readline"); 4: const args = process.argv.slice(2); 5: const filePath = args[0]; 6: const pattern = args[1]; 8: const readStream = fs.createReadStream(filePath, { encoding: "utf8" }); 9: const rl = readline.createInterface({ input: readStream }); 16: console.log(`${lineIndex}: ${line}`);
はい。
それにしても、慣れないですねぇ…。