DirectoryStreamを利用したファイル検索に関するメモ

Java7では新たにDirectoryStreamが追加されていますが、これがなかなか便利なので簡単にメモを残すことに。

もっとも単純なサンプルは、以下のようにただディレクトリ内のコンテンツを出力するものかと思います。

final Path path = FileSystems.getDefault().getPath("path_to_dir");

try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path)) {
    for (Path file : directoryStream) {
        System.out.println(file.toString());
    }
} catch (IOException e) {
    e.printStackTrace();
}

名前にStreamと付くので当然ながら利用後はクローズする必要があります。ここではtry-with-resourcesを利用しています。

このプログラムを実行すると、Pathオブジェクトが示すディレクトリの情報を出力します。

DirectoryStreamが便利なのはFiles#newDirectoryStreamメソッドの第2引数に取得するコンテンツを選別するためのフィルタリング定義を行うことができる点です。

Globbing patternによるパターンマッチングを行い、パターンに一致するファイルを取得するには以下のように記載します。

DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path, "hoge*.txt");

上記のように宣言した場合Pathオブジェクトが示すディレクトリ内に存在するファイルのうち、ファイル名がhoge*.txtのパターンに一致するファイルのみを取得することが可能です。

このように検索をするプログラムをずいぶん簡単に記述することができます。

さらにDirectoryStream.Filterインターフェースを実装し独自のフィルタクラスを作成し、newDirectoryStreamメソッドの第2引数に設定することでより詳細なフィルタリング処理を行うことが可能です。

たとえば、ファイルの内容に特定の文字列が含まれているファイルのみをフィルタリングしてリストアップするといったことも容易に行えるようになります。

final Path path = FileSystems.getDefault().getPath("path_to_dir");
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(
        path, new DirectoryStream.Filter<Path>() {

            @Override
            public boolean accept(Path entry) throws IOException {
                boolean result = false;
                try(BufferedReader reader = Files.newBufferedReader(entry, Charset.defaultCharset())) {
                    Pattern pattern = Pattern.compile("hoge.*");
                    String line = null;
                    while ((line = reader.readLine()) != null) {
                        Matcher matcher = pattern.matcher(line);
                        if (matcher.matches()) {
                            result = true;
                            break;
                        }
                    }
                }
                return result;
            }
        })) {
    for (Path file : directoryStream) {
        System.out.println(file.getFileName());
    }
} catch (IOException e) {
    e.printStackTrace();
}

上記プログラムはサンプルなのでかなり適当です。try-with-resources内で無名クラスとしてFilterインターフェースを実装しているので無駄に長くて可読性がよくないですが、サンプルなので。。。
このプログラムは、DirectoryStream.Filterインターフェースのacceptメソッド内で取得したPathオブジェクトが示すファイルを読み込み、"hoge.*"という文字列が存在すればtrueを返し、存在しなければfalseを返すことで、対象のフィルタリングを行っています。

個人的に思いついた利用シーンは、特定の文字列を含むファイルの検索はもちろんですが、ファイル内容を読み込み、自然言語解析及び統計的手法を用いて特定のキーワドに対して意味的に関連のあるファイルを抽出するといったところでしょうか。