質問: 前提となる文字コードやバイナリに関しての知識が怪しいので、間違っている点を指摘してもらえるとありがたいです。 まず`fs.createReadStream(, {encoding})`はテキストファイルを読み込む場合に使用するオプションであり、読み込んだデータを指定した文字コードの文字列に変換する機能で、default値がnullなのはバイナリファイルを読み込むことを想定したものだと認識しています。 次に`fs.createWriteStream(, {encoding})`は書き込むのがバイナリファイルならnullを、テキストファイルなら事前に変換する文字コードを指定する機能なのだと認識しています。 ここで疑問なのですが、`fs.createReadStream`のそれとは違いdefault値がnullではなく"utf8"になっています。 標準で`fs.createReadStream`はバイナリファイル、`fs.createWriteStream`はテキストファイルを扱うよう分かれているのは混乱の元だと思うのですが、何故このような仕様になっているのでしょうか。
回答: 結論としては、`fs.createReadStream`と`fs.createWriteStream`では`encoding`オプションの意味が少し違います。 前提として、`fs.createReadStream`や`fs.createWriteStream`については、**ファイル**がバイナリかテキストかということではなく、それによって読み書きされる**データ**がバイナリかテキストかということを考えたほうがよいです。 どんなファイルであっても、結局中身はバイナリデータに過ぎません。そのバイナリデータがUTF-8などの文字エンコーディングに従っている場合に我々はそれがテキストファイルであると認識します。 まず`fs.createWriteStream`については、`encoding`オプションは「データとして**文字列**が渡されたときにその文字列をどのエンコーディングで**バイナリデータ**に変換するか」を指定する意味を持ちます。`encoding`オプションにどのような値が渡されていたとしても、直接用意した**バイナリデータ**をファイルに保存したい場合は`Buffer`オブジェクトを用意すればOKです。 ですから、そのような場合、つまり(テキストファイルではなく)バイナリファイルを作成したい場合でも、`fs.createWriteStream`の`encoding`オプションに`null`などを渡す必要はありません。 よって、`fs.createWriteStream`の`encoding`オプションのデフォルト値は、**文字列**を渡されたときにそれをどの文字エンコーディングで扱うのが普通かという観点から考えるべきでしょう。 これについては明確に議論しているドキュメントは残念ながら見つけられませんでしたが、現代で国際的にまともに使用可能なのはUnicodeだけであり、ASCIIとの親和性などの観点から、一般的な用途で最も利用が推奨されているのはUTF-8です。ですから、文字列のエンコーディングのデフォルト値にふさわしいのはUTF-8なのは明らかかと思います。一応、node.jsのソースコードに参考になるコメントがあります(3行目)。 ``` // Crypto is kind of old and crusty. Historically, its default string // encoding is 'binary' so we have to make this configurable. // Everything else in the universe uses 'utf8', though. this.defaultEncoding = options.defaultEncoding || 'utf8'; ``` `fs.createReadStream`については、`encoding`を指定すると、読み込んだ**バイナリデータ**を自動的に**文字列**に変換してくれるという便利機能であると考えられます。 ですから、読み込んだデータを**バイナリデータ**(`Buffer`)として渡してほしいのか、それとも**文字列**として渡して欲しいのかという観点から`encoding`の指定を考えることになります。 利便性の観点からいえばデータを**文字列**として欲しいのは扱っているファイルがテキストファイルの場合でしょうから、ファイルがバイナリファイルかテキストファイルかという観点は強ち間違いとも言えません。 この場合、確かにデフォルトを`"utf8"`などにしておいて、`Buffer`でデータが欲しい場合はオプションで明示するというデザインも可能かと思います。 これに関しても残念ながら私は明確な答えを見つけることができませんでしたが、少なくともnode.jsのデザインはそのようになっていません。 理由に関しては推測ですが、わざわざファイルをストリームで読み込む場合はバッファで欲しいことが多い(テキストファイルの場合でも文字列処理を行わない場合はわざわざ**バイナリデータ**をデコードする処理を挟む必要がない)ことや、`encoding`という名前のオプションでデータを`Buffer`として扱うことを明示するというのは分かりにくいことが挙げられます。 実際、現在の`ReadableStream`のデザインでは、`setEncoding`を呼んだら(たとえ`setEncoding(null)`だとしても)データは**文字列**となり、データを**バイナリデータ**(`Buffer`)で欲しい場合は`setEncoding`を全く呼ばないことが求められます。