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

ファイルの検索

シェル・スクリプトの使用経験があれば、パターン・マッチングを使用してファイルを検索したことがあるでしょう。 それも、何度もあるのではないでしょうか。 使用経験がない方のために紹介すると、パターン・マッチングとは特殊文字を使用してパターンを作成し、ファイル名をそのパターンと比較する手法のことです。 ほとんどのシェル・スクリプトで使用されている特殊文字の例として、任意の数の文字を表すアスタリスク(*)があります。 たとえば次のコマンドは、現在のディレクトリにある、名前が.htmlで終わるすべてのファイルを表示します。

% ls *.html

java.nio.fileパッケージにより、この便利な機能がプログラムで使用できるようになります。 各ファイル・システム実装でPathMatcherが用意されています。 ファイル・システムのPathMatcherを取得するには、FileSystemクラスのgetPathMatcher(String)メソッドを使用します。 次のコードでは、デフォルトのファイル・システムのPathMatcherを取得しています。

String pattern = ...;
PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + pattern);

getPathMatcherの文字列引数として、構文の種類と、一致させるパターンを渡します。 この例ではグロブ構文を指定しています。 グロブ構文について詳しくない場合は、グロブについてを参照してください。

グロブ構文は使用しやすく柔軟性に優れていますが、必要に応じて正規表現(regex)構文を使用することもできます。 正規表現に関する詳細は、『Regular Expressions』レッスンを参照してください。 ファイル・システム実装によっては、その他の構文がサポートされる場合があります。

他の形式の文字列ベース・パターン・マッチングを使用する場合は、独自のPathMatcherクラスを作成できます。 このページで紹介するサンプルでは、グロブ構文を使用します。

PathMatcherインスタンスを作成したら、それに対してファイルが一致するかを確認できます。 PathMatcherインタフェースにはmatchesという1つのメソッドが含まれています。このメソッドはPathを引数として受け入れ、パターンに一致するか一致しないかを表すboolean値を返します。 次のコードでは、名前が.javaまたは.classで終わるファイルを検索し、これらのファイル名を標準出力に出力します。

PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.{java,class}");

Path filename = ...;
if (matcher.matches(filename)) {
    System.out.println(filename);
}

再帰的なパターン・マッチング

特定のパターンに一致するファイルの検索は、ファイル・ツリーの探索と密接に関係します。 ファイルがファイル・システムのどこかにあると分かっているのに、その場所が分からないという経験はありませんか。 また、ファイル・ツリー内にある、特定のファイル拡張子を持つすべてのファイルを探し出す必要に迫られたことはないでしょうか。

サンプル・プログラムのFindは、まさしくこの処理を示しています。 FindはUNIXのfindユーティリティと類似していますが、機能が削減されています。 このサンプルを拡張して、他の機能を含めることができます。 たとえば、findユーティリティでは、-pruneオプションを使用して、検索対象からサブツリー全体を除外できます。 この機能は、preVisitDirectoryメソッドでSKIP_SUBTREEを返すことで実装できます。 また、シンボリック・リンクをたどる-Lオプションの機能を実装するには、4つの引数を持つwalkFileTreeメソッドを使用し、この引数としてFOLLOW_LINKS列挙定数を渡します(ただし、循環リンクがないことをvisitFileメソッドで必ずテストしてください)。

このFindアプリケーションを実行するには、次の形式を使用します。

% java Find <path> -name "<glob_pattern>"

パターンは、ワイルド・カードがシェルによって解釈されないように、引用符で囲みます。 次に例を示します。

% java Find . -name "*.html"

以下は、Findサンプル・プログラムのソース・コードです。

/**
 * 指定されたグロブ・パターンと一致するファイルを検索するサンプル・コード。
 * グロブ・パターンの構成に関する詳細は、次の説明を参照してください。
 * fileOps.html#glob
 *
 * パターンに一致するファイルまたはディレクトリは、標準出力に出力されます。
 * また、一致した数も出力されます。
 *
 * このアプリケーションを実行するときは、シェルがワイルド・カードを展開しないように、
 * 次のようにグロブ・パターンを引用符で囲む必要があります。
 *              java Find . -name "*.java"
 */

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import static java.nio.file.FileVisitResult.*;
import static java.nio.file.FileVisitOption.*;
import java.util.*;


public class Find {

    public static class Finder extends SimpleFileVisitor<Path> {
        private final PathMatcher matcher;
        private int numMatches = 0;

        Finder(String pattern) {
            matcher = FileSystems.getDefault().getPathMatcher("glob:" + pattern);
        }

        //グロブ・パターンとファイル名またはディレクトリ名を比較します。
        void find(Path file) {
            Path name = file.getFileName();
            if (name != null && matcher.matches(name)) {
                numMatches++;
                System.out.println(file);
            }
        }

        //一致した総数を標準出力に出力します。
        void done() {
            System.out.println("Matched: " + numMatches);
        }

        //各ファイルに対してパターン・マッチング・メソッドを呼び出します。
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
            find(file);
            return CONTINUE;
        }

        //各ディレクトリに対してパターン・マッチング・メソッドを呼び出します。
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
            find(dir);
            return CONTINUE;
        }

        @Override
        public FileVisitResult visitFileFailed(Path file, IOException exc) {
            System.err.println(exc);
            return CONTINUE;
        }
    }

    static void usage() {
        System.err.println("java Find <path> -name \"<glob_pattern>\"");
        System.exit(-1);
    }

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

        if (args.length < 3 || !args[1].equals("-name"))
            usage();

        Path startingDir = Paths.get(args[0]);
        String pattern = args[2];

        Finder finder = new Finder(pattern);
        Files.walkFileTree(startingDir, finder);
        finder.done();
    }
}

ファイル・ツリーの再帰的な探索については、ファイル・ツリーの探索で説明しています。


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

前のページ: ファイル・ツリーの探索
次のページ: ディレクトリの変更監視