このページを正しく表示するには、ブラウザでJavaScriptを有効にする必要があります。
コース: 重要なクラス
レッスン: 基本的なI/O
セクション: ファイルI/O(NIO.2を含む)
サブセクション: Pathクラス
パス操作
ホームページ > 重要なクラス > 基本的なI/O

パス操作

Pathクラスには、パスに関する情報の取得、パスの要素へのアクセス、パスの他の形式への変換、またはパスの内容の抽出に使用できるさまざまなメソッドが含まれています。 また、パス文字列を照合するためのメソッドや、パスの冗長部分を削除するためのメソッドもあります。 このレッスンでは、これらのPathメソッドについて取り上げます。Pathメソッドはパス自体を操作し、ファイル・システムにはアクセスしないため、統語処理と呼ばれることもあります。

このセクションでは次の内容について説明します。

パスの作成

Pathインスタンスには、ファイルまたはディレクトリの場所を示す情報が含まれます。 この情報は、Pathを定義するときに、1つ以上のファイル名またはディレクトリ名を続けた文字列を引数として指定します。 この文字列にはルート要素とファイル名を含めることができますが、どちらも必須ではありません。 また、Pathは単一のディレクトリ名またはファイル名のみで構成される場合もあります。

Pathオブジェクトは、Paths(複数形であることに注意してください)ヘルパー・クラスにあるgetメソッドのいずれかを使用して簡単に作成できます。

Path p1 = Paths.get("/tmp/foo");
Path p2 = Paths.get(args[0]);
Path p3 = Paths.get(URI.create("file:///Users/joe/FileTest.java"));

Paths.getメソッドは、次のコードの省略表現です。

Path p4 = FileSystems.getDefault().getPath("/users/sally");

次のコードでは、ホーム・ディレクトリが/u/joeであるとすると、/u/joe/logs/foo.log(Windowsの場合はC:\joe\log\foo.log)が作成されます。

Path p5 = Paths.get(System.getProperty("user.home"), "logs", "foo.log");

パスに関する情報の取得

Pathは、名前要素を1つのシーケンスとして保存するものと考えることができます。 ディレクトリ構造内の最上位要素はインデックス0で表され、ディレクトリ構造内の最下位要素はインデックス[n-1]nPath内の名前要素の個数)となります。 このインデックスを使用してPathの個別要素またはサブシーケンスを取得するメソッドが用意されています。

このレッスンで取り上げるサンプルでは、次のディレクトリ構造を使用します。

Sample directory structure

サンプルのディレクトリ構造

次のコードでは、Pathインスタンスを定義し、いくつかのメソッドを呼び出して、パスに関する情報を取得しています。

// 次のメソッドはいずれも、Pathに対応するファイルが存在している
// 必要はありません。
Path path = Paths.get("C:\\home\\joe\\foo");    // Microsoft Windowの構文
//Path path = Paths.get("/home/joe/foo");       // Solarisの構文
System.out.format("toString: %s%n", path.toString());
System.out.format("getFileName: %s%n", path.getFileName());
System.out.format("getName(0): %s%n", path.getName(0));
System.out.format("getNameCount: %d%n", path.getNameCount());
System.out.format("subpath(0,2): %s%n", path.subpath(0,2));
System.out.format("getParent: %s%n", path.getParent());
System.out.format("getRoot: %s%n", path.getRoot());
WindowsとSolaris OSにおける出力結果は次のとおりです。

呼び出したメソッド Solaris OSでの出力結果 Microsoft Windowsでの出力結果 説明
toString /home/joe/foo C:\home\joe\foo Pathの文字列表現が返されます。 対象のパスがFilesystems.getDefault().getPath(String)または(getPathの簡易版メソッドである)Paths.getを使用して作成されたものである場合は、このメソッドにより、パス文字列の簡単な正規化が行われます。 たとえば、UNIXオペレーティング・システムでは、//home/joe/fooという入力文字列は/home/joe/fooに修正されます。
getFileName foo foo ファイル名、または名前要素シーケンスの最後の要素が返されます。
getName(0) home home 指定したインデックスに対応するパス要素が返されます。 0番目の要素はルートにもっとも近いパス要素です。
getNameCount 3 3 パス内の要素数が返されます。
subpath(0,2) home/joe home\joe 開始インデックスと終了インデックスで指定したPathのサブシーケンス(ルート要素は含まない)が返されます。
getParent /home/joe \home\joe 親ディレクトリのパスが返されます。
getRoot / C:\ パスのルートが返されます。

上のサンプル・コードは、絶対パスの出力について表しています。 次に示すコードでは、相対パスを指定します。

Path path = Paths.get("sally/bar");     // Solarisの構文
または
Path path = Paths.get("sally\\bar");    // Microsoft Windowsの構文

WindowsとSolaris OSにおける出力結果は次のとおりです。

呼び出したメソッド Solaris OSでの出力結果 Microsoft Windowsでの出力結果
toString sally/bar sally\bar
getFileName bar bar
getName(0) sally sally
getNameCount 2 2
subpath(0,1) sally sally
getParent sally sally
getRoot null null

パスの冗長部分の削除

多くのファイル・システムでは、現在のディレクトリを示す表現として"."を使用し、親ディレクトリを示す表現として".."を使用しています。 そのため、Pathに冗長なディレクトリ情報が含まれる可能性があります。 たとえば、サーバーが"/dir/logs/."ディレクトリにログ・ファイルを保存するように設定されている場合に、パスの末尾の"/."を削除したいと考えるかもしれません。

次のパスには両方とも、冗長な表現が含まれています。

/home/./joe/foo
/home/sally/../joe/foo
normalizeメソッドを使用すると、"."や"ディレクトリ名/.."などの冗長な要素が削除されます。 上のパスはいずれも、/home/joe/fooに正規化されます。

normalizeでは、パスの冗長部分を削除する際にファイル・システムの実際の状況が確認されるわけではないことに注意してください。 これは純粋な統語処理です。 上の2つ目のパスでsallyがシンボリック・リンクの場合、sally/..を削除すると、Pathが目的のファイルの場所を示さなくなる可能性があります。

パスの冗長部分を削除し、かつ、正しいファイルの場所が確実に示されるようにするには、toRealPathメソッドを使用できます。 このメソッドについては、次のセクションのパスの変換で説明します。

パスの変換

Pathの変換に使用できるメソッドは3つあります。 パスを、ブラウザから開くことのできる文字列に変換する必要がある場合は、toUriを使用できます。 次のように使用します。
Path p1 = Paths.get("/home/logfile");
System.out.format("%s%n", p1.toUri());  // 結果はfile:///home/logfile

toAbsolutePathメソッドは、引数のパスを絶対パスに変換します。 引数のパスがすでに絶対パスの場合は、同じPathオブジェクトが返されます。 toAbsolutePathメソッドは、ユーザーが入力したファイル名を処理する際に非常に便利です。 次のように使用します。

public class FileTest {
    public static void main(String[] args) throws IOException {

	if (args.length < 1) {
	    System.out.println("usage: FileTest file");
	    System.exit(-1);
	}

	// 入力文字列をPathオブジェクトに変換します。
	Path inputPath = Paths.get(args[0]);

	// 入力Pathを絶対パスに変換します。
	// 通常はこの変換によって、パスの先頭に現在の作業ディレクトリが追加されます。
	// このサンプル・プログラムが次のように呼び出されるとします。
	//	       java FileTest foo
	// この場合、"inputPath"インスタンスのgetRootとgetParentの各メソッドでは
	// nullが返されます。絶対パスに変換した"fullPath"インスタンスの
	// getRootとgetParentでは、期待した値が返されます。
	Path fullPath = inputPath.toAbsolutePath();
    }
}

toAbsolutePathメソッドはユーザー入力を変換し、問合せを受けた場合に有益な値を返すPathを返します。 このメソッドは、ファイルが存在していなくても動作します。

toRealPathメソッドは、既存ファイルの実際のパスを返します。 このメソッドは、複数の処理を1回で実行します。

ファイルが存在しない場合やアクセスできない場合は、このメソッドは例外をスローします。 これらの状況に対処するには、例外をキャッチします。 次のように処理します。

try {
    Path fp = path.toRealPath(true);
} catch (NoSuchFileException x) {
    System.err.format("%s: no such file or directory%n", path);
    //ファイルが存在しない場合のロジック
} catch (IOException x) {
    System.err.format("%s%n", x);
    //その他のファイル・エラーの場合のロジック
}

2つのパスの結合

resolveメソッドを使用すると、パスを結合できます。 部分パス(ルート要素を含まないパス)を渡すと、その部分パスが元のパスに追加されます。

たとえば、次のコードについて考えてみましょう。

Path p1 = Paths.get("/home/joe/foo");          // Solaris
System.out.format("%s%n", p1.resolve("bar"));  // 結果は/home/joe/foo/bar

または

Path p1 = Paths.get("C:\\home\\joe\\foo");    // Microsoft Windows
System.out.format("%s%n", p1.resolve("bar")); // 結果はC:\home\joe\foo\bar
resolveメソッドの引数として絶対パスを渡すと、渡したパスが返されます。
Paths.get("foo").resolve("/home/joe");       // 結果は/home/joe

2つのパス間でのパスの作成

ファイルI/Oコードを記述しているときに、ファイル・システム内のある場所から別の場所へのパスが必要になることはよくあります。 この要件には、relativizeメソッドを使用して対応できます。 このメソッドにより、元のパスから、引数のパスが指定する場所までのパスが構成されます。 新しいパスは、元のパスに対する相対パスになります。

たとえば、joeおよびsallyとして定義された2つの相対パスについて考えます。

Path p1 = Paths.get("joe");
Path p2 = Paths.get("sally");
その他の情報がない場合は、joesallyは兄弟ノード、すなわちツリー構造の同じレベルに位置するノードであると想定されます。 したがって、joeからsallyに移動するには、まず1つ上位の親ノードに移動し、次にsallyに下がると考えることができます。
Path p1_to_p2 = p1.relativize(p2);   // 結果は../sally
Path p2_to_p1 = p2.relativize(p1);   // 結果は../joe
次に、少し複雑な例について考えます。
Path p1 = Paths.get("home");
Path p3 = Paths.get("home/sally/bar");
Path p1_to_p3 = p1.relativize(p3);  // 結果はsally/bar
Path p3_to_p1 = p3.relativize(p1);  // 結果は../..
この例では、2つのパスが同じノードであるhomeを共有しています。 homeからbarに移動するには、まず1つ下位のsallyに移動し、次にさらに1つ下位のbarに移動します。 barからhomeに移動するには、2つ上位に移動する必要があります。

どちらか一方のパスのみにルート要素が含まれる場合は、相対パスは構成できません。 両方のパスにルート要素が含まれる場合は、相対パスを構成できるかどうかはシステムによって異なります。

再帰的なサンプル・プログラムのCopyでは、relativizeメソッドとresolveメソッドを使用しています。

2つのパスの比較

Pathクラスでは、equalsメソッドを使用して、2つのパスが等しいかどうかをチェックできます。 また、startsWithメソッドを使用してパスが特定の文字列から始まるかをチェックし、endsWithメソッドを使用して特定の文字列で終わるかをチェックできます。 これらのメソッドの使用法は簡単です。 次のように使用します。

Path path = ...;
Path otherPath = ...;
Path beginning = Paths.get("/home");
Path ending = Paths.get("foo");

if (path.equals(otherPath)) {
    //2つのパスが等しい場合のロジック
} else if (path.startsWith(beginning)) {
    //パスが"/home"で始まる場合のロジック
} else if (path.endsWith(ending)) {
    //パスが"foo"で終わる場合のロジック
}

Pathクラスは、Iterableインタフェースを実装しています。 iteratorメソッドにより、パス内の名前要素を反復処理できるオブジェクトが返されます。 返される最初の要素はディレクトリ・ツリー内のルートにもっとも近い要素です。 次のコードでは、パスを反復処理して、各名前要素を出力します。

Path path = ...;
for (Path name: path) {
    System.out.println(name);
}

Pathクラスは、Comparableインタフェースも実装しています。 compareToを使用してPathオブジェクトを比較できます。このメソッドはソート処理に便利です。

PathオブジェクトをCollectionに格納することもできます。 この強力な機能に関する詳細は、Collectionsコースを参照してください。

2つのPathオブジェクトが同じファイルを指しているかどうかをチェックする場合は、isSameFileメソッドを使用できます。このメソッドについては、2つのパスの示すファイルが同一かどうかのチェックで説明しています。


サンプル・プログラムで問題が発生した場合は、 Compiling and Running the Examples: FAQsを参照してください。
フィードバックをお寄せください。さまざまなご意見をお待ちしております。

前のページ: Pathクラス
次のページ: ファイル操作