事象
いきなり個人的な事情で申し訳ないのですが、一連のファイルやディレクトリに対して、globを使ってフィルタリングしたいという場合、Files::newDirectoryStream(Path, String)
を利用してきました。というより、その方法しか知りませんでした。
このAPIでは第2引数にglobを指定します。以下がその例になります。
var dir = Paths.get(".");
try (var paths = Files.newDirectoryStream(dir, "**.py")) {
for (var path : paths) {
doSomething(path);
}
} catch (IOException e) {
e.printStackTrace();
}
正直なところDirectoryStream
は使いにくいAPIで、とくに「java.util.Stream
につなげづらい」というのが、個人的にはもっともつらい。__要するに「globによるフィルタリングをStream処理に組み込みたい」わけです。__そこで、いろいろと調べてまわったところ、java.nio.file.PathMatcher
を利用すればよいということが判明。
var dir = Paths.get(".");
var matcher = FileSystems.getDefault().getPathMatcher("**.py");
Files.walk(dir).filter(matcher::matches).forEach(path -> doSomething(path));
さっそくこれを実行したところ、以下のようにIllegalArgumentException
が発生し、想定通りに動作しませんでした。
Exception in thread "main" java.lang.IllegalArgumentException
at java.base/sun.nio.fs.WindowsFileSystem.getPathMatcher(WindowsFileSystem.java:262)
at Main.main(Main.java:19)
原因と対処
今回は「JavaDocをちゃんと読みましょう」案件でした…。FileSystem::getPathMatcher(String)
のJavaDocには次の通り記述されています。
syntaxAndPattern パラメータは、構文とパターンを識別し、次の形式をとります。
syntax:pattern
ここでの ':' はそれ自体を表します。
FileSystem 実装では、「glob」および「regex」構文をサポートしますが、その他をサポートすることもできます。構文コンポーネントの値は大文字小文字に関係なく比較されます。
今回のようにglobパターンを利用したい場合はglob:**.py
というような書き方をする必要があるということです。それを使って上記の例を書き換えたものが以下になり、これを実行するとIllegalArgumentException
が発生しなくなりました。
var dir = Paths.get(".");
var matcher = FileSystems.getDefault().getPathMatcher("glob:**.py");
Files.walk(dir).filter(matcher::matches).forEach(path -> doSomething(path));