Javaでワイルドカード文字列に一致するファイルを見つける方法は?


157

これは本当に簡単なはずです。このような文字列がある場合:

../Test?/sample*.txt

次に、このパターンに一致するファイルのリストを取得するために一般的に受け入れられている方法は何ですか?(たとえば、一致する必要が../Test1/sample22b.txtあります../Test4/sample-spiffy.txtが、一致しない../Test3/sample2.blah必要があります../Test44/sample2.txt

私は見ましたがorg.apache.commons.io.filefilter.WildcardFileFilter、それは正しい獣のようですが、相対ディレクトリパスでファイルを見つけるためにそれを使用する方法がわかりません。

ワイルドカード構文を使用しているので、antのソースを調べることができると思いますが、ここではかなり明白なものが欠落しているに違いありません。

編集:上記の例は単なるサンプルケースです。実行時にワイルドカードを含む一般的なパスを解析する方法を探しています。mmyersの提案に基づいてそれを行う方法を理解しましたが、それは一種の煩わしいことです。 Java JREは、main(String []引数)の単純なワイルドカードを単一の引数から自動解析して時間と手間を「節約」するようです...にファイル以外の引数がなかったことをうれしく思いますミックス。)


2
これは、Javaではなく、ワイルドカードを解析するシェルです。それらをエスケープすることはできますが、正確な形式はシステムによって異なります。
マイケルマイヤーズ

2
いいえ、ちがいます。Windowsは*ワイルドカードを解析しません。ダミーのバッチファイルで同じ構文を実行し、.objファイルでいっぱいのディレクトリを指すTest / *。objである引数#1を出力して、これを確認しました。「Test / *。obj」を出力します。Javaはここで奇妙なことをしているようです。
Jason S

ええ、あなたは正しいです。ほとんどすべての組み込みシェルコマンドはワイルドカードを展開しますが、シェル自体は展開しません。とにかく、Javaがワイルドカードを解析しないように、引数を引用符で囲むことができます。javaMyClass "Test / *。obj"
Michael Myers

3
6年以上後、スクロールが苦手でJavaの> = 7ゼロ深度ソリューションが必要な方は、@ Vadzimで以下の回答を参照して投票するか、docs.oracle.com / javase
earcam

回答:


81

Apache AntのDirectoryScannerを検討してください。

DirectoryScanner scanner = new DirectoryScanner();
scanner.setIncludes(new String[]{"**/*.java"});
scanner.setBasedir("C:/Temp");
scanner.setCaseSensitive(false);
scanner.scan();
String[] files = scanner.getIncludedFiles();

ant.jarを参照する必要があります(ant 1.7.1の場合は約1.3 MB)。


1
優れた!ところで、scanner.getIncludedDirectories()は、ディレクトリが必要な場合に同じことを行います。(getIncludedFilesは機能しません)
Tilman Hausherr

1
githubのワイルドカードプロジェクトも魅力のように機能します:github.com/EsotericSoftware/wildcard
Moreaki

1
コメントではなく別の回答として属する@Moreaki
Jason S

これとまったく同じものDirectoryScannerplexus-utils(241Kb)にあります。ant.jar(1.9Mb)よりも小さいです。
Verhagen

これは機能します。しかしls、同じファイルパターン(ls <pattern>DirectoryScannerを使用した場合のミリ秒と分)に比べて非常に遅いようです...
dokaspar

121

Apache commons-io(とメソッド)FileUtilsから試してください:listFilesiterateFiles

File dir = new File(".");
FileFilter fileFilter = new WildcardFileFilter("sample*.java");
File[] files = dir.listFiles(fileFilter);
for (int i = 0; i < files.length; i++) {
   System.out.println(files[i]);
}

TestXフォルダーに関する問題を解決するには、まずフォルダーのリストを反復処理します。

File[] dirs = new File(".").listFiles(new WildcardFileFilter("Test*.java");
for (int i=0; i<dirs.length; i++) {
   File dir = dirs[i];
   if (dir.isDirectory()) {
       File[] files = dir.listFiles(new WildcardFileFilter("sample*.java"));
   }
}

かなり「力ずくの」解決策ですが、うまく機能するはずです。これがニーズに合わない場合は、いつでもRegexFileFilterを使用できます。


2
さて、これで、Jason Sが質問を投稿したときの正確な場所に到達しました。
マイケルマイヤーズ

かなりではありません。使用できるRegexFileFilterもあります(ただし、個人的に使用する必要はありませんでした)。
ウラジミール

57

ここで駆動パターンによるファイルリストの例ですNIOのJava 7には、 グロブ 8つのラムダとJava:

    try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(
            Paths.get(".."), "Test?/sample*.txt")) {
        dirStream.forEach(path -> System.out.println(path));
    }

または

    PathMatcher pathMatcher = FileSystems.getDefault()
        .getPathMatcher("regex:Test./sample\\w+\\.txt");
    try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(
            new File("..").toPath(), pathMatcher::matches)) {
        dirStream.forEach(path -> System.out.println(path));
    }

13
またはFiles.walk(Paths.get("..")).filter(matcher::matches).forEach(System.out::println);
amoebe

@Qstnr_La、はい、補助ラムダとメソッド参照を除きます。
Vadzim 2018年

29

ワイルドカード文字列を正規表現に変換し、それをStringのmatchesメソッドで使用できます。あなたの例に従ってください:

String original = "../Test?/sample*.txt";
String regex = original.replace("?", ".?").replace("*", ".*?");

これはあなたの例ではうまくいきます:

Assert.assertTrue("../Test1/sample22b.txt".matches(regex));
Assert.assertTrue("../Test4/sample-spiffy.txt".matches(regex));

そして反例:

Assert.assertTrue(!"../Test3/sample2.blah".matches(regex));
Assert.assertTrue(!"../Test44/sample2.txt".matches(regex));

3
これは、(、+または$
djjeck

'String regex = "^" + s.replace( "?"、 "。?")。replace( " "、 "。?")+ "$"'を使用しました(アスタリスクは何らかの理由でコメントに表示されなくなりました)。 ..)
Jouni Aro 2014

2
なぜ*を '。*に置き換えるのですか?? isFileMatchTargetFilePatternブールのpublic static(最終ファイルF、最終列targetPattern){ ``文字列の正規表現= targetPattern.replace( "\\。" ""); ` regex = regex.replace("?", ".?").replace("* ", ".*"); return f.getName().matches(regex); }
トニー

OPが「ワイルドカードを含む一般的なパス」を要求したため、より多くの特殊文字を引用する必要があります。私はむしろPattern.quoteを使用したいと思います:StringBuffer regexBuffer = ...; Matcher matcher = Pattern.compile("(.*?)([*?])").matcher(original); while (matcher.find()) { matcher.appendReplacement(regexBuffer, (Pattern.quote(matcher.group(1)) + (matcher.group(2).equals("*") ? ".*?" : ".?")).replace("\\", "\\\\").replace("$", "\\$")); } matcher.appendTail(regexBuffer);
EndlosSchleife '14

補遺:「?」は必須の文字を表すので、の.代わりに置き換える必要があり.?ます。
EndlosSchleife 2018

23

Java 8以降ではFiles#find、から直接メソッドを使用できますjava.nio.file

public static Stream<Path> find(Path start,
                                int maxDepth,
                                BiPredicate<Path, BasicFileAttributes> matcher,
                                FileVisitOption... options)

使用例

Files.find(startingPath,
           Integer.MAX_VALUE,
           (path, basicFileAttributes) -> path.toFile().getName().matches(".*.pom")
);

1
例を拡張して、ストリームに保持されている最初の一致のパスを出力するようにできますか?
jxramos 2018

18

現時点では役に立たないかもしれませんが、JDK 7は「その他のNIO機能」の一部として、globとregexのファイル名を一致させることを目的としています。


3
Java 7の場合:Files.newDirectoryStream(path、glob-pattern)
Pat Niemeyer

13

ワイルドカードライブラリは、globとregexの両方のファイル名照合を効率的に実行します。

http://code.google.com/p/wildcard/

実装は簡潔です-JARはわずか12.9キロバイトです。


2
唯一の欠点は、Maven Centralにないことです
yegor256

3
それはOSSです。MavenCentralに配置してください。:)
NateS 2013年

10

外部インポートを使用しない簡単な方法は、この方法を使用することです

billing_201208.csv、billing_201209.csv、billing_201210.csvという名前のcsvファイルを作成しましたが、正常に動作しているようです。

上記のファイルが存在する場合、出力は次のようになります

found billing_201208.csv
found billing_201209.csv
found billing_201210.csv

    //インポートを使用->インポートjava.io.File
        public static void main(String [] args){
        文字列pathToScan = "。";
        文字列target_file; // fileThatYouWantToFilter
        ファイルfolderToScan = new File(pathToScan); 

    File[] listOfFiles = folderToScan.listFiles();

     for (int i = 0; i < listOfFiles.length; i++) {
            if (listOfFiles[i].isFile()) {
                target_file = listOfFiles[i].getName();
                if (target_file.startsWith("billing")
                     && target_file.endsWith(".csv")) {
                //You can add these files to fileList by using "list.add" here
                     System.out.println("found" + " " + target_file); 
                }
           }
     }    
}


6

別の回答に投稿されているように、ワイルドカードライブラリはglobとregexの両方のファイル名照合で機能します:http : //code.google.com/p/wildcard/

次のコードを使用して、* nixスタイルのファイルシステムで絶対パターンと相対パターンを含むグロブパターンを照合しました。

String filePattern = String baseDir = "./";
// If absolute path. TODO handle windows absolute path?
if (filePattern.charAt(0) == File.separatorChar) {
    baseDir = File.separator;
    filePattern = filePattern.substring(1);
}
Paths paths = new Paths(baseDir, filePattern);
List files = paths.getFiles();

私はこれを実行するために、Apache commons ioライブラリ(Vladimirの回答を参照)のFileUtils.listFilesメソッドを取得するのに少し時間を費やしましたが、成功しませんでした(現在、/一度に1つのディレクトリまたはファイルに一致するパターンしか処理できないと思います) 。

さらに、ファイルシステム全体を検索せずに、ユーザーが指定した任意の絶対タイプグロブパターンを処理するために正規表現フィルター(ファビアンの回答を参照)を使用すると、最大の非正規表現/グロブプレフィックスを決定するために、提供されたグロブの前処理が必要になります。

もちろん、Java 7は要求された機能を適切に処理する可能性がありますが、残念ながら今のところ私はJava 6にこだわっています。ライブラリは、サイズが13.5kbと比較的小さいです。

レビューアへのメモ:このライブラリについて言及している既存の回答に上記を追加しようとしましたが、編集は拒否されました。これをコメントとして追加するのに十分な担当者もいません。より良い方法はありません...


プロジェクトを別の場所に移行する予定はありますか?code.google.com/p/support/wiki/ReadOnlyTransitionを
Luc M

1
'私のプロジェクトではありません。すでに移行されているようです:github.com/EsotericSoftware/wildcard
Oliver Coleman

5

を使用できるはずWildcardFileFilterです。使用System.getProperty("user.dir")して、作業ディレクトリを取得します。これを試して:

public static void main(String[] args) {
File[] files = (new File(System.getProperty("user.dir"))).listFiles(new WildcardFileFilter(args));
//...
}

あなたは交換する必要はないはず*[.*]、ワイルドカードフィルタの使用を想定し、java.regex.Pattern。私はこれをテストしていませんが、パターンとファイルフィルターを常に使用しています。



3

Apacheフィルターは、既知のディレクトリー内のファイルを反復するために作成されています。ディレクトリでワイルドカードも使用できるようにするには、 ' \'または ' /'でパスを分割し、各部分に個別にフィルターをかける必要があります。


1
これはうまくいきました。それは少し迷惑でしたが、特にトラブルが発生しやすいというわけではありませんでした。ただし、グロブマッチングのためのJDK7の機能を楽しみにしています。
Jason S

0

なぜ次のようなことをしないでください:

File myRelativeDir = new File("../../foo");
String fullPath = myRelativeDir.getCanonicalPath();
Sting wildCard = fullPath + File.separator + "*.txt";

// now you have a fully qualified path

そうすれば、相対パスを気にする必要がなくなり、必要に応じてワイルドカードを使用できます。


1
相対パスにもワイルドカードを含めることができるためです。
Jason S


0

Utilメソッド:

public static boolean isFileMatchTargetFilePattern(final File f, final String targetPattern) {
        String regex = targetPattern.replace(".", "\\.");  //escape the dot first
        regex = regex.replace("?", ".?").replace("*", ".*");
        return f.getName().matches(regex);

    }

jUnitテスト:

@Test
public void testIsFileMatchTargetFilePattern()  {
    String dir = "D:\\repository\\org\my\\modules\\mobile\\mobile-web\\b1605.0.1";
    String[] regexPatterns = new String[] {"_*.repositories", "*.pom", "*-b1605.0.1*","*-b1605.0.1", "mobile*"};
    File fDir = new File(dir);
    File[] files = fDir.listFiles();

    for (String regexPattern : regexPatterns) {
        System.out.println("match pattern [" + regexPattern + "]:");
        for (File file : files) {
            System.out.println("\t" + file.getName() + " matches:" + FileUtils.isFileMatchTargetFilePattern(file, regexPattern));
        }
    }
}

出力:

match pattern [_*.repositories]:
    mobile-web-b1605.0.1.pom matches:false
    mobile-web-b1605.0.1.war matches:false
    _remote.repositories matches:true
match pattern [*.pom]:
    mobile-web-b1605.0.1.pom matches:true
    mobile-web-b1605.0.1.war matches:false
    _remote.repositories matches:false
match pattern [*-b1605.0.1*]:
    mobile-web-b1605.0.1.pom matches:true
    mobile-web-b1605.0.1.war matches:true
    _remote.repositories matches:false
match pattern [*-b1605.0.1]:
    mobile-web-b1605.0.1.pom matches:false
    mobile-web-b1605.0.1.war matches:false
    _remote.repositories matches:false
match pattern [mobile*]:
    mobile-web-b1605.0.1.pom matches:true
    mobile-web-b1605.0.1.war matches:true
    _remote.repositories matches:false

ファイルシステムパスでテキスト検索を使用することはできません。そうでなければfoo/bar.txt一致しfoo?bar.txt、それは正しくありません
Jason S

Jason私はパスを含まないfile.getName()を使用しました。
トニー

:それは私が与えたパターン例では動作しません../Test?/sample*.txt
ジェイソンS

0
Path testPath = Paths.get("C:\");

Stream<Path> stream =
                Files.find(testPath, 1,
                        (path, basicFileAttributes) -> {
                            File file = path.toFile();
                            return file.getName().endsWith(".java");
                        });

// Print all files found
stream.forEach(System.out::println);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.