ファイルを読み込む

前のセクションではコマンドライン引数を受け取って、Node.jsのスクリプト中で利用できるようになりました。 このセクションではコマンドライン引数に指定されたファイルを読み込んで、標準出力に表示してみましょう。

ファイルパスを受け取る

まずは読み込むファイルのパスを受け取ります。前回のセクションでインストールしたcommanderを使って、コマンドライン引数からファイルパスを取得しましょう。 オプションでないコマンドライン引数はprogram.args配列から取得できます。 次のように、ファイルパスを取得して標準出力に表示します。

const program = require("commander");

program.parse(process.argv);
const filePath = program.args[0];
console.log(filePath);

このスクリプトを次のように実行すると、引数に与えたファイルパスがそのまま表示されます。

$ node receive-path.js sample.md
sample.md

fsモジュールを使ってファイルを読み込む

取得したファイルパスをもとに、ファイルを読み込みます。 Node.jsでファイルの読み書きをおこなうには、標準モジュールのfsモジュールを使います。 まずは読み込むためのファイルを作成しましょう。sample.mdという名前でreceive-path.jsと同じディレクトリに配置します。

# sample

fsモジュール

fsモジュールは、Node.jsでファイルの読み書きをおこなうための基本的な関数を提供するモジュールです。 すべての関数は非同期形式と同期形式の両方が提供されています。 非同期形式の関数は常にコールバック関数を受け取ります。 コールバック関数の第1引数は必ずその処理で発生したエラーオブジェクトになっていて、処理の戻り値などがその後ろの引数につづきます。 処理が成功したときには、第1引数はnullまたはundefinedになります。 一方、同期形式の関数が処理に失敗したときは例外を発生させるので、try/catch文によって例外処理をおこなうことができます。

次のサンプルコードは非同期形式の関数の例です。

const fs = require("fs");

fs.readFile("sample.md", (err, file) => {
});

そして、次のサンプルコードは同じ関数の同期形式の例です。

const fs = require("fs");

try {
    const file = fs.readFileSync("sample.md");
} catch (err) { 
}

Node.jsはシングルスレッドなので、ノンブロッキング処理である非同期形式のAPIを選ぶことがほとんどです。 Node.jsにはfsモジュール以外にも多くの非同期APIがあるので、非同期処理に慣れておきましょう。

readFile関数を使う

ファイルを読み込むには、fsモジュールのreadFile関数を使います。 readFile関数にはいくつかのオーバーロードがあります。 次の例では、ファイルパスとコールバック関数だけを渡していますが、 このときのコールバック関数の第2引数はBufferインスタンスになります。

const program = require("commander");
const fs = require("fs");

program.parse(process.argv);
const filePath = program.args[0];

fs.readFile(filePath, (err, file) => {
    console.log(file);
});

sample.mdを引数に渡した実行結果は次のようになります。 Bufferインスタンスはファイルの中身をバイト列として保持していて、そのままconsole.log関数に渡しても人間が読める文字列にはなりません。

$ node read-file-1a.js sample.md
<Buffer 23 20 73 61 6d 70 6c 65>

Bufferインスタンスから文字列を取り出すには、toStringメソッドを使います。 toStringメソッドはオプショナルな引数として文字エンコーディングを受け取れますが、 何も指定しなかった場合は自動的にUTF-8として変換されます。 次の例ではコールバック関数の中でtoStringメソッドを呼び出して、ファイルの中身をUTF-8の文字列として表示します。

const program = require("commander");
const fs = require("fs");

program.parse(process.argv);
const filePath = program.args[0];

fs.readFile(filePath, (err, file) => {
    console.log(file.toString());
});

sample.mdを引数に渡した実行結果は次のようになります。

$ node read-file-1b.js sample.md
# sample

ちなみに、次の例のようにreadFile関数の第2引数で文字エンコーディング形式を指定できます。 このときのコールバック関数の第2引数は、指定した文字エンコーディングでエンコードされた後の文字列が渡されます。

const program = require("commander");
const fs = require("fs");

program.parse(process.argv);
const filePath = program.args[0];

fs.readFile(filePath, "utf8", (err, file) => {
    console.log(file);
});

sample.mdを引数に渡した実行結果は次のようになります。

$ node read-file-2.js sample.md
# sample

エラーハンドリング

先述のとおり、fsモジュールのコールバック関数の第1引数には常にエラーオブジェクトが渡されます。 ファイルの読み書きは存在の有無や権限、ファイルシステムの違いなどによって例外が発生しやすいので、必ずエラーハンドリング処理を書きましょう。 次の例はerrオブジェクトがnullまたはundefinedではないことだけをチェックするシンプルなエラーハンドリングです。 エラーが発生していたときには表示し、process.exit関数を使って実行しているプロセスを異常終了させています。

const program = require("commander");
const fs = require("fs");

program.parse(process.argv);
const filePath = program.args[0];

fs.readFile(filePath, "utf8", (err, file) => {
    if (err) {
        console.error(err);
        process.exit(err.code);
        return;
    }
    console.log(file);
});

存在しないファイルであるnotfound.mdを引数に渡すと、次のようにエラーが発生して終了します。

$ node read-file-3.js notfound.md
Error: ENOENT: no such file or directory, open 'notfound.md'

これでコマンドライン引数に指定したファイルを読み込んで標準出力に表示できました。