node.jsに入門してみる。3 ~ファイルの読み書き編~

node.js 入門の続き。WebSocketをはじめる前に、データの読み書きができないと様々不便だと思ったので、やってみた。

ドキュメントのファイルシステムの節を参考に、ファイルの読み書きをやってみる。

導入

まず最初に、ファイルシステムモジュール 'fs' のインスタンスを作成する。

var fs = require('fs');

この fs にファイルの読み書きをするための機能が詰め込まれているっぽい。

「同期」と「非同期」がある

関数名をざっと眺めると、それぞれ ****Sync() という関数が対で用意されている(例:fs.readFile()fs.readFileSync() など)。 Sync が付いていない方が「非同期」の関数で、付いている方が「同期」の関数のようだ。

ドキュメントには、非同期メソッドでは順序の保証はありません、また、非同期の形式は常に最後の引数として完了コールバックを受け取ります とある。おそらく、

非同期
コマンドの実行完了を待たず、スクリプトはどんどん進む。並列処理。
同期
コマンドの実行が終わってから次へ進む。直列処理。

ということなんじゃないかと思う。

さて、実際に読み書きをしてみたいと思うが、ドキュメントに File I/O は POSIX 標準の関数に対する単純なラッパーとして提供されます とあるとおり、基本的にできることは他の言語と同じので、全部はやらない。

ファイルの内容を「読み込む」、「上書き」、「追記」をやってみようと思う。

ファイルの情報を知る

前準備として、ファイルに関する情報の調べ方を調べておく。次のようにして取得できる。

var stat = fs.statSync('./sample.txt');

fs.statSync() が返した値 statconsole.log() してみると、次のような値が格納されている。

{ dev: 2049,
  ino: 491851,
  mode: 33188,
  nlink: 1,
  uid: 1000,
  gid: 1000,
  rdev: 0,
  size: 1556,
  blksize: 4096,
  blocks: 8,
  atime: Mon, 12 Sep 2011 05:59:41 GMT,
  mtime: Mon, 12 Sep 2011 05:59:40 GMT,
  ctime: Mon, 12 Sep 2011 05:59:40 GMT }

例えば size はファイルサイズを示している。

それから、与えられたパスがファイルであれば、stat.isFile()true を返し、ディレクトリであれば stat.isDirectory()true を返す。扱う対象を確認するのに使える。

ファイルの内容を読み込む

Reading files with node.js(RUMINATIONS ON CODE)を参考に組んでみた。参考ソースは、ファイルを1行ずつ読み込む外部モジュールとして設計されているようだが、ここではもっとうんと簡略化した実装をしてみる。

細かいところで幾つか謎はあるけど、一応、テキストファイルの読み込みに成功した。

function fileGetContents( filename ){
	var fs = require("fs");
	var fileContent = "";
	var stat = fs.statSync(filename);

	var fd = fs.openSync(filename, "r");
	var bytes = fs.readSync(fd, stat.size, 0, "ascii");
	fileContent += bytes[0];
	fs.closeSync(fd);

	return fileContent;
};
console.log( fileGetContents( './sample.txt' ) );

fs.openSync() でファイルを開いて、fs.readSync() で内容を読み取り、fs.closeSync() で閉じる、という手続きを踏んでいる。

fs.openSync() の第2引数に r を指定している。r は読み込み専用モードの意味。

fs.readSync() の第2引数にバッファーサイズを指定するが、ここでは、読み込むファイルのサイズを調べ(stat.size)、全量を1回で読み込みきるようにした。実際はこのままだと、巨大なファイルを取り扱う場合に難があるかも知れないが、ひとまずよし。

ちなみにこの実装では、なぜか読み込んだテキストの最後に改行(?)が1つ追加されたような感じになってしまう。この改行が何なのかわからないが、EOFか何かなのだろうか? 正体がわからないので上記のソースではそのままにしているが、バッファサイズを stat.size-1 とすれば、とりあえずそれらしい動きにはなる。参考までに。

・・・と思いきや、これを1発でやってくれる便利なラッパーがあった。

var fs = require("fs");
console.log( fs.readFileSync( './sample.txt' ) );

ファイルを上書き保存する

開く→書き込む→閉じるの手続き。読み込みの時と概ね似た手続きだが、fs.openSync() の第2引数は w になった。w は書き込み専用モード。

fs.readSync() だったところは fs.writeSync() にしている。fs.writeSync() の第2引数に、保存したい文字列を渡す。

function fileSaveContents( filename , str ){
	var fs = require("fs");

	var fd = fs.openSync(filename, "w");
	fs.writeSync(fd, str, 0, "ascii");
	fs.closeSync(fd);

	return true;
};
console.log( fileSaveContents( './sample.txt' , 'sample text.' ) );

上書き保存にも、1発でいける便利なメソッドが用意されていたので、こっちを使った方がよさそう。

var fs = require("fs");
fs.writeFileSync( './sample.txt' , 'contents' );

ファイルに追記する

開く→書き込む→閉じるの手続き。ほぼ同じ。

追記の場合の fs.openSync() は、aモードでオープンする。

function filePutContents( filename , str ){
	var fs = require("fs");

	var fd = fs.openSync(filename, "a");
	fs.writeSync(fd, str, 0, "ascii");
	fs.closeSync(fd);

	return true;
};
filePutContents( './sample2.txt' , 'sample text.'+"\n" );

追記する際に、最後に改行コード "\n" を付けてないと、1行がどんどん長くなってしまうので注意。

追記を1発でやってくれる便利なラッパーは・・・一見、ないように見える。

まとめ

とりあえず、簡易的ではあるけれども、ファイルの読み書きができるようになった。ここまでで、簡単なウェブアプリの開発はできる要素がそろったと思う。

実際に、WebSocketなどを使った複雑なアプリケーションを開発する場合は、読み込みや書き込みのステータス管理などをちゃんとやらないと、処理の順番の問題とか出てくると思う。そしたら、非同期系の処理を上手く使う必要が出てくるのかな、多分。


プロフィール

コヤナギ トモヤ

ウェブ系エンジニアしてます。ウェブデザイナー、ウェブディレクターとしてウェブ制作の仕事に携わり、今はエンジニア職に流れ着きました。誰かのお仕事をちょっとだけ効率化するような支援ツールの開発が好き。オープンソースとMITライセンス大好き。人生後半は自由と民主主義のコントリビューターとして過ごす予定。

ウェブ制作支援ツール Pickles 2 をオープンソースで開発しています。

PHP/JavaScript/NodeJS/nwjs/Laravel/Pickles2/オープンソース/心理学/倫理/自由と民主主義

RSSフィード

ページの先頭へ戻る