如何在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无法解析*通配符。我已经通过在虚拟批处理文件上运行相同的语法并打印出#1参数Test / *。obj来指向充满.obj文件的目录,来检查了这一点。它输出“ Test / *。obj”。Java在这里似乎有些奇怪。
杰森S

嗯,你是对的。几乎所有内置的shell命令都会扩展通配符,但是shell本身不会。无论如何,您可以将参数放在引号中,以防止Java解析通配符:java MyClass“ Test / *。obj”
Michael Myers

3
6年后,对于那些讨厌滚动并且想要Java> = 7零深度解决方案的人,请 @Vadzim 下方查看并提高答案,或者在docs.oracle.com/javase/tutorial/essential/io上冗长地打扰
摄像头

Answers:


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上的通配符项目也很有
吸引力

1
@Moreaki属于单独的答案,而不是评论
Jason S

这完全一样DirectoryScanner被发现丛-utils的(241KB)。小于ant.jar(1.9Mb)。
Verhagen

这可行。但是,ls与具有相同文件模式的相比,它似乎非常慢(ls <pattern>使用DirectoryScanner时使用毫秒,而不是分钟)...
dokaspar

121

FileUtilsApache commons-iolistFilesiterateFiles方法)尝试:

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

以下是按Java 7 nio globlob和Java 8 lambdas 支持的模式列出文件的示例:

    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);
变形虫

@Qstnr_La,是的,除了辅助lambda和方法引用。
Vadzim '18

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
为什么用*替换*。? public static boolean isFileMatchTargetFilePattern(最终文件f,最终String targetPattern){``String regex = targetPattern.replace(“。”,“ \\。”);` regex = regex.replace("?", ".?").replace("* ", ".*"); return f.getName().matches(regex); }
Tony

由于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

附录:“?” 表示必填字符,因此应将其替换..?
EndlosSchleife

23

从Java 8开始,您可以Files#find直接从中使用method 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

18

现在可能无法为您提供帮助,但是JDK 7旨在使glob和regex文件名匹配,作为“更多NIO功能”的一部分。


3
在Java 7中:Files.newDirectoryStream(path,glob-pattern)
Pat Niemeyer


10

不使用任何外部导入的简单方法是使用此方法

我创建了以billing_201208.csv,billing_201209.csv,billing_201210.csv命名的csv文件,看起来工作正常。

如果上面列出的文件存在,则输出将为以下内容

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

    //使用Import-> import java.io.File
        公共静态void main(String [] args){
        字符串pathToScan =“。”;
        字符串target_file; // fileThatYouWantToFilter
        File folderToScan =新文件(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/

我使用以下代码来匹配glob模式,包括* 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库中获取FileUtils.listFiles方法(请参见弗拉基米尔的回答),但没有成功(我意识到现在/认为它一次只能处理与一个目录或文件匹配的模式)。 。

另外,使用正则表达式过滤器(请参阅Fabian的答案)来处理用户提供的任意绝对类型的glob模式而不搜索整个文件系统,将需要对提供的glob进行一些预处理,以确定最大的非regex / glob前缀。

当然,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的glob匹配功能。
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
因为相对路径也可以具有通配符。
杰森S


0

使用方法:

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
Jason 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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.