Revised: Mar./10th/2002; Since: Jan./5th/2002
FileStreams の節では、ファイルに対する入出力のストリームを紹介しました。そこでは、ファイルをオブジェクトとして扱いました。本節では、ファイルオブジェクトについて、もう少し説明します。
継承関係:
java.lang.Object | +--java.io.File
コンストラクタ:
File(File parent, String child) File(String pathname) File(String parent, String child)
いずれにせよ、JavaVM を実装しているネイティブのファイルシステムにおける、ファイルかディレクトリのパス名を引数にとります。それをオブジェクト化した File オブジェクトは抽象パス名とよばれ、これを引数に取ることも可能です。
このクラスのオブジェクトは変更することが出来ません。より自由に変更したい場合は、 RandomAccessFile クラスを使います。
パスは文字列、またはオブジェクトで与えますが、絶対パスと相対パスが使えます。相対パスを解決する基準ディレクトリは、 JavaVM が実行されたディレクトリになります。区切り文字はファイルシステムに依存し、 UNIX 系では /、 Windows 系では \\ を使います。デフォルトの区切り文字は、 System の file.separator を参照することで分かります。
System.out.println(System.getProperty("file.separator"));
このクラスには、 IO のエラーを事前にチェックするためのメソッドが沢山用意されています。詳細は API 仕様書を御覧頂くとして、ここでは重要なものをいくつかピックアップします。
| 戻り値 | メソッド | 説明 |
|---|---|---|
boolean | canRead() |
この抽象パス名が示すファイルをアプリケーションが読み込めるかどうかを判定します。 |
boolean | canWrite() |
この抽象パス名が示すファイルをアプリケーションが変更できるかどうかを判定します。 |
boolean | createNewFile() |
この抽象パス名が示す空の新しいファイルを不可分 (atomic) に生成します (そのファイルがまだ存在しない場合だけ)。 |
boolean | delete() |
この抽象パス名が示すファイルまたはディレクトリを削除します。 |
boolean | exists() |
この抽象パス名が示すファイルが存在するかどうかを判定します。 |
boolean | isDirectory() |
この抽象パス名が示すファイルがディレクトリであるかどうかを判定します。 |
boolean | isFile() |
この抽象パス名が示すファイルが普通のファイルかどうかを判定します。 |
String[] | list() |
この抽象パス名が示すディレクトリにあるファイルおよびディレクトリを示す文字列の配列を返します。 |
boolean | mkdir() |
この抽象パス名が示すディレクトリを生成します。 |
boolean | setReadOnly() |
この抽象パス名が示すファイルまたはディレクトリにマークを設定し、読み込みオペレーションだけが許可されるようにします。 |
一見すると、 UNIX におけるファイル操作の基本コマンドは概ね用意されているようです。
次の例は、コマンドラインからディレクトリを指定するパスを受け取り、その内容を再帰的 (recursive) に出力するものです。 UNIX 系コマンドの ls -r と同じです。
import java.io.*;
class LsRec {
public static void main(String[] args) {
//デフォルトのパスはカレント
String path = ".";
//引数があればパス名と解釈
if (args.length != 0) {
path = args[0];
}
//パス名のオブジェクト化
File dir = new File(path);
//ディレクトリかどうか判定
if (!dir.isDirectory()) {
System.out.println("ディレクトリではないか、"
+ "または存在しません。");
return;
}
//再帰的リスト
recursive(dir, 0);
}
//再帰的リストのメソッド
static void recursive(File dir, int indent) {
//ディレクトリの内容の取得
String[] contents = dir.list();
//その出力
for (int i=0; i < contents.length; i++) {
//インデント
for (int j=0; j < indent; j++) {
System.out.print(" ");
}
System.out.println(contents[i]);
//サブパスのオブジェクト化
File sdir = new File(dir, contents[i]);
//ディレクトリならば再帰的にリスト
if (sdir.isDirectory()) {
recursive(sdir, indent+1);
}
}
}
}
recursive() をメソッドで作りましたが、クラスを別に作ったほうがスッキリするはずです。
C:\IO>javac LsRec.java
C:\IO>java LsRec C:\\JavaSmpl
IO
Cat.java
CatEUC.class
CatEUC.java
Cat.class
copy.gif
GUI
HowdyGUI.java
HowdyGUI.class
SimpleExample.java
SimpleExample$RadioListener.class
SimpleExample$1.class
SimpleExample.class
API_Test1.java
AllFont.class
AllFont.java
API_Test1.class
allclasses-frame.html
C:\IO>
これを少し修正して、引数で受け取った文字列を含むファイルやディレクトリを探し、存在すればその絶対パスを返すようなプログラムを作ってみます。
考え方としては、上のサンプルの、再帰的リストのメソッドで、出力する部分を、マッチングのオブジェクトに投げ込んで判定します。
FindName.java:
import java.io.*;
class FindName {
private static Match match = new Match();
public static void main(String[] args) {
//コマンドライン引数のチェック
if (args.length == 0) {
System.out.println("検索語句を与えてください。");
return;
}
//検索文字列のセット
match.setNames(args);
//ルートのオブジェクト化
String sep = System.getProperty("file.separator");
File dir = new File(sep);
//再帰的リスト
recursive(dir, 0);
}
//再帰的リストのメソッド
static void recursive(File dir, int indent) {
//ディレクトリの内容の取得
String[] contents = dir.list();
//その出力
for (int i=0; i < contents.length; i++) {
match.chk(dir, contents[i]);
//サブパスのオブジェクト化
File sdir = new File(dir, contents[i]);
//ディレクトリならば再帰的にリスト
if (sdir.isDirectory()) {
recursive(sdir, indent+1);
}
}
}
}
class Match {
private String[] name;
private int i;
public void setNames(String[] args) {
name = args;
}
boolean chk(File path, String leaf) {
String fullPath
= path.getAbsolutePath()
+ System.getProperty("file.separator")
+ leaf;
for (int i=0; i < name.length; i++) {
if (fullPath.indexOf(name[i]) == -1) {
// fullPath を leaf に書き換えれば、
// ファイル/ディレクトリ名だけにマッチング
return false;
}
}
System.out.print(++i + ": ");
System.out.println(fullPath);
return true;
}
}
ファイル/ディレクトリのフルパスから、検索文字列全てにヒットしたものを出力します。マッチングをフルパスに対して行いましたが、毎回チェックしているので効率が良くありません。if (fullPath.indexOf(name[i]) == -1) の条件文を leaf.indexOf(name[i]) == -1 にして、ファイル名だけにマッチさせるか、ディレクトリに対するマッチングは別に行う方が良いでしょう。
C:\IO>javac FindName.java C:\IO>java FindName IO java 1: C:\Java\IO\Test.java 2: C:\Java\VectorIO.java 3: C:\WINNT\java\trustlib\com\ms\mtx\IObjectContext.class 4: C:\WINNT\java\trustlib\com\ms\mtx\IObjectControl.class 5: C:\WINNT\java\trustlib\com\ms\mtx\IObjectContextInfo.class 6: C:\JavaSmpl\IO\Cat.java 7: C:\JavaSmpl\IO\CatEUC.java 8: C:\IO\Copy.java 9: C:\IO\Cat.java 10: C:\IO\CCopyEUC2JIS.java 11: C:\IO\CatEUC.java 12: C:\IO\CCopy.java 13: C:\IO\Nl.java 14: C:\IO\PipeTest.java
File はパス最後に蛇足を述べます。
File クラスをインスタンス化したオブジェクトは、 JavaVM を実装しているネィティブなファイルシステム(Windows や UNIX や Linux など)のファイル/ディレクトリを参照するために使います。しかし、ファイルの実体を表しているのではなく、ネイティブなファイルシステムにおけるパスを表すオブジェクトです。そのために、 File クラス型オブジェクトは抽象パスと呼ばれます。
従って、ファイルだけでなくディレクトリのパスも表せますし、存在しないパスも表現できます。また、このオブジェクトに null を代入しても、表現していたパスに存在するファイルやディレクトリがネイティブのファイルシステム上から削除されるわけではありません。