Java NIO2パスAPI

Java NIO2 Path API

1. 概要

この記事では、Javaで新しいI / O(NIO2)PathAPIを使用する方法を学習します。

NIO2のPath APIは、Java 7に付属する主要な新しい機能領域の1つであり、具体的には、ファイルAPIと並んで新しいファイルシステムAPIのサブセットです。

2. セットアップ

NIO2サポートはjava.nio.fileパッケージにバンドルされています。 したがって、Path APIを使用するようにプロジェクトを設定するには、このパッケージのすべてをインポートするだけです。

import java.nio.file.*;

この記事のコードサンプルはおそらくさまざまな環境で実行されるため、ユーザーのホームディレクトリのハンドルを取得しましょう。

private static String HOME = System.getProperty("user.home");

この変数は、あらゆる環境の有効な場所を指します。

Pathsクラスは、ファイルシステムパスに関連するすべての操作への主要なエントリポイントです。 これにより、ファイルおよびディレクトリへのパスを作成および操作できます。

注目に値するのは、パス操作は本質的に主に構文であるということです。それらは、基礎となるファイルシステムに影響を与えず、ファイルシステムが成功したか失敗したかに影響しません。 これは、存在しないパスをパス操作のパラメーターとして渡すことは、それが成功するか失敗するかに関係ないことを意味します。

3. パス操作

このセクションでは、パス操作で使用される主な構文を紹介します。 その名前が示すように、Pathクラスは、ファイルシステム内のパスをプログラムで表現したものです。

Pathオブジェクトには、パスの作成に使用されるファイル名とディレクトリリストが含まれ、ファイルの検査、検索、および操作に使用されます。

ヘルパークラスjava.nio.file.Paths(複数形)は、Pathオブジェクトを作成する正式な方法です。 パス文字列からPathを作成するための2つの静的メソッドがあります。

Path path = Paths.get("path string");

パスString,でスラッシュを使用するかバックスラッシュを使用するかは関係ありませんが、APIは、基になるファイルシステムの要件に従ってこのパラメーターを解決します。

そして、java.net.URIオブジェクトから:

Path path = Paths.get(URI object);

これで、これらの動作を確認できます。

4. パスの作成

パス文字列からcreate a Pathオブジェクトへ:

@Test
public void givenPathString_whenCreatesPathObject_thenCorrect() {
    Path p = Paths.get("/articles/example");

    assertEquals("\\articles\\example", p.toString());
}

get APIは、最初の部分(この場合はarticles)に加えて、パス文字列部分(この場合はarticlesexample)の可変引数パラメーターを受け取ることができます。 )。

完全なパス文字列の代わりにこれらの部分を提供する場合、それらはPathオブジェクトを構築するために使用され、変数引数部分に名前区切り文字(スラッシュ)を含める必要はありません。

@Test
public void givenPathParts_whenCreatesPathObject_thenCorrect() {
    Path p = Paths.get("/articles", "example");

    assertEquals("\\articles\\example", p.toString());
}

5. パス情報の取得

Pathオブジェクトは、シーケンスとしての名前要素と考えることができます。 E:\examplerticles\javaなどのパスStringは、3つの名前要素で構成されます。 examplearticles、およびjava。 ディレクトリ構造の最上位の要素はインデックス0にあり、この場合はexampleです。

ディレクトリ構造の最下位の要素はインデックス[n-1]にあります。ここで、nはパス内の名前要素の数です。 この最も低い要素は、実際のファイルであるかどうかに関係なく、file nameと呼ばれます。

@Test
public void givenPath_whenRetrievesFileName_thenCorrect() {
    Path p = Paths.get("/articles/example/logs");

    Path fileName = p.getFileName();

    assertEquals("logs", fileName.toString());
}

インデックスによって個々の要素を取得するためのメソッドが利用可能です:

@Test
public void givenPath_whenRetrievesNameByIndex_thenCorrect() {
    Path p = Paths.get("/articles/example/logs");
    Path name0 = getName(0);
    Path name1 = getName(1);
    Path name2 = getName(2);
    assertEquals("articles", name0.toString());
    assertEquals("example", name1.toString());
    assertEquals("logs", name2.toString());
}

または、これらのインデックス範囲を使用したパスのサブシーケンス:

@Test
public void givenPath_whenCanRetrieveSubsequenceByIndex_thenCorrect() {
    Path p = Paths.get("/articles/example/logs");

    Path subPath1 = p.subpath(0,1);
    Path subPath2 = p.subpath(0,2);

    assertEquals("articles", subPath1.toString());
    assertEquals("articles\\example", subPath2.toString());
    assertEquals("articles\\example\\logs", p.subpath(0, 3).toString());
    assertEquals("example", p.subpath(1, 2).toString());
    assertEquals("example\\logs", p.subpath(1, 3).toString());
    assertEquals("logs", p.subpath(2, 3).toString());
}

各パスは親パスに関連付けられています。パスに親がない場合はnullが関連付けられます。 パスオブジェクトの親は、パスのルートコンポーネント(存在する場合)と、ファイル名を除くパス内の各要素で構成されます。 例として、/a/b/cの親パスは/a/bであり、/aの親パスはnullです。

@Test
public void givenPath_whenRetrievesParent_thenCorrect() {
    Path p1 = Paths.get("/articles/example/logs");
    Path p2 = Paths.get("/articles/example");
    Path p3 = Paths.get("/articles");
    Path p4 = Paths.get("/");

    Path parent1 = p1.getParent();
    Path parent2 = p2.getParent();
    Path parent3 = p3.getParent();
    Path parent4 = p4.getParenth();

    assertEquals("\\articles\\example", parent1.toString());
    assertEquals("\\articles", parent2.toString());
    assertEquals("\\", parent3.toString());
    assertEquals(null, parent4);
}

パスのルート要素も取得できます。

@Test
public void givenPath_whenRetrievesRoot_thenCorrect() {
    Path p1 = Paths.get("/articles/example/logs");
    Path p2 = Paths.get("c:/articles/example/logs");

    Path root1 = p1.getRoot();
    Path root2 = p2.getRoot();

    assertEquals("\\", root1.toString());
    assertEquals("c:\\", root2.toString());
}

6. パスの正規化

多くのファイルシステムは、“.”表記を使用して現在のディレクトリを示し、“..”を使用して親ディレクトリを示します。 パスに冗長なディレクトリ情報が含まれる場合があります。

たとえば、次のパス文字列を考えます。

/example/./articles
/example/authors/../articles
/example/articles

それらはすべて同じ場所/example/articlesに解決されます。 最初の2つには冗長性があり、最後の2つにはありません。

パスを正規化するには、パス内の冗長性を削除する必要があります。 Path.normalize()操作は、この目的のために提供されています。

これで、この例は一目瞭然です。

@Test
public void givenPath_whenRemovesRedundancies_thenCorrect1() {
    Path p = Paths.get("/home/./example/articles");

    Path cleanPath = p.normalize();

    assertEquals("\\home\\example\\articles", cleanPath.toString());
}

これも:

@Test
public void givenPath_whenRemovesRedundancies_thenCorrect2() {
    Path p = Paths.get("/home/example/../articles");

    Path cleanPath = p.normalize();

    assertEquals("\\home\\articles", cleanPath.toString());
}

7. パス変換

選択したプレゼンテーション形式にパスを変換する操作があります。 パスをブラウザから開くことができる文字列に変換するには、toUriメソッドを使用します。

@Test
public void givenPath_whenConvertsToBrowseablePath_thenCorrect() {
    Path p = Paths.get("/home/example/articles.html");

    URI uri = p.toUri();
    assertEquals(
      "file:///E:/home/example/articles.html",
        uri.toString());
}

パスを絶対表現に変換することもできます。 toAbsolutePathメソッドは、ファイルシステムのデフォルトディレクトリに対するパスを解決します。

@Test
public void givenPath_whenConvertsToAbsolutePath_thenCorrect() {
    Path p = Paths.get("/home/example/articles.html");

    Path absPath = p.toAbsolutePath();

    assertEquals(
      "E:\\home\\example\\articles.html",
        absPath.toString());
}

ただし、解決されるパスがすでに絶対パスであることが検出された場合、メソッドはそれをそのまま返します。

@Test
public void givenAbsolutePath_whenRetainsAsAbsolute_thenCorrect() {
    Path p = Paths.get("E:\\home\\example\\articles.html");

    Path absPath = p.toAbsolutePath();

    assertEquals(
      "E:\\home\\example\\articles.html",
        absPath.toString());
}

toRealPathメソッドを呼び出すことにより、任意のパスを実際の同等のパスに変換することもできます。 このメソッドは、パスの要素をファイルシステム内の実際のディレクトリとファイルにマッピングすることにより、パスを解決しようとします。

ファイルシステム内のログインユーザーのホームロケーションを指すSetupセクションで作成した変数を使用する時間:

@Test
public void givenExistingPath_whenGetsRealPathToFile_thenCorrect() {
    Path p = Paths.get(HOME);

    Path realPath = p.toRealPath();

    assertEquals(HOME, realPath.toString());
}

上記のテストでは、この操作の動作についてあまり説明していません。 最も明白な結果は、パスがファイルシステムに存在しない場合、操作はIOExceptionをスローし、続行することです。

このポイントを家に戻すより良い方法がないため、次のテストを見てください。これは、存在しないパスを実際のパスに変換しようとします。

@Test(expected = NoSuchFileException.class)
public void givenInExistentPath_whenFailsToConvert_thenCorrect() {
    Path p = Paths.get("E:\\home\\example\\articles.html");

    p.toRealPath();
}

IOExceptionをキャッチすると、テストは成功します。 この操作がスローするIOExceptionの実際のサブクラスは、NoSuchFileExceptionです。

8. パスの結合

任意の2つのパスを結合するには、resolveメソッドを使用します。

簡単に言えば、任意のPathresolveメソッドを呼び出し、引数としてpartial pathを渡すことができます。 その部分パスは元のパスに追加されます。

@Test
public void givenTwoPaths_whenJoinsAndResolves_thenCorrect() {
    Path p = Paths.get("/example/articles");

    Path p2 = p.resolve("java");

    assertEquals("\\example\\articles\\java", p2.toString());
}

ただし、resolveメソッドに渡されるパス文字列がpartial path;の最も顕著な絶対パスではない場合、渡されたパスが返されます。

@Test
public void givenAbsolutePath_whenResolutionRetainsIt_thenCorrect() {
    Path p = Paths.get("/example/articles");

    Path p2 = p.resolve("C:\\example\\articles\java");

    assertEquals("C:\\example\\articles\\java", p2.toString());
}

ルート要素を持つパスでも同じことが起こります。 パス文字列“java”にはルート要素がありませんが、パス文字列“/java”にはルート要素があります。 したがって、ルート要素を含むパスを渡すと、そのまま返されます。

@Test
public void givenPathWithRoot_whenResolutionRetainsIt_thenCorrect2() {
    Path p = Paths.get("/example/articles");

    Path p2 = p.resolve("/java");

    assertEquals("\\java", p2.toString());
}

9. Relativizing Paths

relativizingという用語は、単に2つの既知のパス間に直接パスを作成することを意味します。 たとえば、ディレクトリ/exampleがあり、その中に/example/authors/example/articlesが有効なパスであるような他の2つのディレクトリがあります。

authorsに対するarticlesへのパスは、“move one level up in the directory hierarchy then into articles directory”または..rticles:として記述されます。

@Test
public void givenSiblingPaths_whenCreatesPathToOther_thenCorrect() {
    Path p1 = Paths.get("articles");
    Path p2 = Paths.get("authors");

    Path p1_rel_p2 = p1.relativize(p2);
    Path p2_rel_p1 = p2.relativize(p1);

    assertEquals("..\\authors", p1_rel_p2.toString());
    assertEquals("..\\articles", p2_rel_p1.toString());
}

articlesディレクトリをauthorsフォルダに移動して、兄弟ではなくなったと仮定します。 次の相対化操作には、examplearticlesの間、およびその逆のパスの作成が含まれます。

@Test
public void givenNonSiblingPaths_whenCreatesPathToOther_thenCorrect() {
    Path p1 = Paths.get("/example");
    Path p2 = Paths.get("/example/authors/articles");

    Path p1_rel_p2 = p1.relativize(p2);
    Path p2_rel_p1 = p2.relativize(p1);

    assertEquals("authors\\articles", p1_rel_p2.toString());
    assertEquals("..\\..", p2_rel_p1.toString());
}

10. パスの比較

Pathクラスには、equalsメソッドの直感的な実装があり、2つのパスが等しいかどうかを比較できます。

@Test
public void givenTwoPaths_whenTestsEquality_thenCorrect() {
    Path p1 = Paths.get("/example/articles");
    Path p2 = Paths.get("/example/articles");
    Path p3 = Paths.get("/example/authors");

    assertTrue(p1.equals(p2));
    assertFalse(p1.equals(p3));
}

パスが特定の文字列で始まるかどうかも確認できます。

@Test
public void givenPath_whenInspectsStart_thenCorrect() {
    Path p1 = Paths.get("/example/articles");

    assertTrue(p1.startsWith("/example"));
}

または、他の文字列で終了します:

@Test
public void givenPath_whenInspectsEnd_thenCorrect() {
    Path p1 = Paths.get("/example/articles");

    assertTrue(p1.endsWith("articles"));
}

11. 結論

この記事では、Java 7の一部として出荷された新しいファイルシステムAPI(NIO2)のパス操作を示し、それらのほとんどが動作していることを確認しました。

この記事で使用されているコードサンプルは、記事のGithub projectにあります。