2-4-3: Fileクラスとファイルシステムの操作


・ java.ioパッケージには Fileという名前のクラスが存在します。 このクラスは単にファイルを表すオブジェクトを提供するだけではありません。 ファイルやディレクトリの情報の取得、新しいディレクトリの作成、 ファイルやディレクトリの削除など、 ファイルシステムを操作するためのさまざまな機能を与えてくれます。 その主な機能をまとめておきましょう。

ファイルおよびディレクトリに関する情報
メソッド名働き
boolean exists() ファイル(ディレクトリ)が存在するかどうかの判定
boolean isFile()通常のファイルであるかどうかの判定
boolean isDirectory()ディレクトリであるかどうかの判定
boolean canWrite()書き込み可能かどうかの判定
boolean canRead()読み込み可能かどうかの判定
long length()ファイルのサイズ(バイト数)を返す
long lastModified() ファイルのタイムスタンプを表す数値を返す

ファイルおよびディレクトリの操作
メソッド名働き
String[] list()
String[] list(FilenameFilter filter)
ディレクトリ内のファイル名の一覧を返す
boolean mkdir()
boolean mkdirs()
オブジェクトに対応するディレクトリを作成する
boolean renameTo(File dest) オブジェクトに対応するファイル(ディレクトリ)のパス名を、 引数に与えられたオブジェクトのパス名に変更する
boolean delete() オブジェクトに対応するファイル(ディレクトリ)を削除する

 Fileクラスの機能を利用した サンプルプログラム LsCommand.java を紹介しましょう。 コマンドライン引数で指定されたディレクトリ内のファイル名の一覧を表示します。 UNIXの ls 、MS-DOSの dirコマンドに相当するプログラムです。 ディレクトリ名を省略した場合には カレントディレクトリの内容を調べるようにします。 このサンプルは実質的には Fileクラスの list()メソッドの機能を そのまま呼び出しているにすぎません。(ちょっと簡単すぎたかもしれません。)

・ もう少し歯ごたえのある サンプルプログラム FindCommnad.java も紹介しましょう。 ディレクトリを再帰的に降りていってサブディレクトリをすべて調べるような 処理を考えます。 これは実用上からもしばしば必要になりそうですし、 クラスの設計としても適度な練習問題でしょう。 次のような条件に限定した処理を行います。

つまり UNIXの findコマンドの機能を単純化して実現するものです。 まずサンプルの前半部分、 フィールドの宣言とコンストラクタの定義から解説しましょう。 探索の対象となる各ディレクトリに対応して、 1個のオブジェクトを生成するのが自然な設計でしょう。 それぞれのオブジェクトは、対応するディレクトリについて以下の情報を保持します。 ディレクトリ自身を表す Fileオブジェクト(current)、 出発点からの相対パスの情報(path)、 ディレクトリ内のファイル名の一覧(names)、 それらに対応する Fileオブジェクトの配列(files)です。 また探索の対象となるファイル名(target)は、 すべてのオブジェクトに共通ですから staticなフィールドとして宣言しておきます。
 コンストラクタが行う主な仕事は次のような内容です。 先ほどの LsCommandと同様にディレクトリ内のファイルの一覧を調べます。 ただしファイル名だけではなく Fileのオブジェクトそのものが必要となるので、 listFiles()メソッドによって Fileのオブジェクトの配列を獲得します。
 パス名に区切りの文字をつけ加える操作の部分で、 File.separator という表現が用いられています。 File.separator はファイルシステムが階層ディレクトリのパスの区切りに 用いる文字を与えます。UNIXならば "/" ですし、Windowsであれば "\" となります。 このようにシステムごとの違いを吸収して、 「システムに依存しないファイルシステムの管理」を実現することも Fileクラスの重要な役割です。
 このコンストラクタには throws および throw というキーワードを用いた 例外オブジェクトの操作が記述されています。 throws はこのメソッドもしくはコンストラクタが発生する可能性のある 例外のクラスを明らかにします。 実際に例外を発生する命令は throw です。 ここでは Fileのオブジェクトの生成で例外が発生すると、 それを「転送する」形で自分自身を呼び出した相手に例外の発生を伝えています。
 次にプログラムの後半部分を見てみましょう。 printName()メソッドは、 現在探索中のディレクトリ内のファイル名の中に探索中のものが存在するかどうかを 調べます。もし存在するならばパス名と合わせて表示します。
 searchSubDirs()メソッドは、 現在探索中のディレクトリ内にサブディレクトリが存在するかどうかを調べます。 もし存在するならば、それらに対応する FindCommand自身のオブジェクトを 生成し、その printName() および searchSubDirs()を呼び出します。 ここが再帰的な探索を実現するポイントの部分です。
 以上で準備は完了です。後は探索をスタートさせるだけです。 その仕事はもちろん main()メソッドが行います。 コマンドラインから受け取った文字列を探索のための名前に記憶し、 カレントディレクトリを出発点にして最初の FindCommandのオブジェクトを 生成します。 実行するにはたとえば以下のようにします。 (もちろん出力結果は環境によって異なります。)


java FindCommand index.html
./index.html
./1st/index.html
./2nd/index.html
./3rd/index.html
./4th/index.html