如何在 Java 中查找与通配符字符串匹配的文件?

Posted

技术标签:

【中文标题】如何在 Java 中查找与通配符字符串匹配的文件?【英文标题】:How to find files that match a wildcard string in Java? 【发布时间】:2010-10-22 02:49:11 【问题描述】:

这应该很简单。如果我有这样的字符串:

../Test?/sample*.txt

那么获取与此模式匹配的文件列表的普遍接受的方法是什么? (例如,它应该匹配 ../Test1/sample22b.txt../Test4/sample-spiffy.txt 但不匹配 ../Test3/sample2.blah../Test44/sample2.txt

我查看了org.apache.commons.io.filefilter.WildcardFileFilter,它似乎是正确的野兽,但我不确定如何使用它在相对目录路径中查找文件。

我想我可以查看 ant 的源代码,因为它使用通配符语法,但我必须在这里遗漏一些非常明显的东西。

(edit:上面的例子只是一个示例。我正在寻找在运行时解析包含通配符的一般路径的方法。我根据 mmyers 的建议想出了如何做到这一点但这有点烦人。更不用说 java JRE 似乎从单个参数自动解析 main(String[] arguments) 中的简单通配符以“节省”我的时间和麻烦......我很高兴我混合中没有非文件参数。)

【问题讨论】:

这是解析通配符的shell,而不是Java。您可以转义它们,但具体格式取决于您的系统。 不,不是。 Windows 不解析 * 通配符。我通过在一个虚拟批处理文件上运行相同的语法并打印出参数 #1 来检查这一点,该参数是 Test/*.obj 指向一个充满 .obj 文件的目录。它打印出“Test/*.obj”。 Java 似乎在这里做了一些奇怪的事情。 呵呵,你说得对;几乎所有内置 shell 命令都扩展通配符,但 shell 本身没有。无论如何,您可以将参数放在引号中以防止 Java 解析通配符: java MyClass "Test/*.obj" 6 年多之后,对于那些讨厌滚动并希望使用 Java >=7 零深度解决方案的人,请参阅@Vadzim 的answer below 并点赞,或者对docs.oracle.com/javase/tutorial/essential/io/find.html 进行详细的讨论/厌烦 【参考方案1】:

使用 io 库的 File 类最简单的方法是:

    String startingdir="The directory name";
    String filenameprefix="The file pattern"
    File startingDirFile=new File(startingdir); 
    final File[] listFiles=startingDirFile.listFiles(new FilenameFilter() 
        public boolean accept(File arg0,String arg1)
        System.out.println(arg0+arg1);
            return arg1.matches(filenameprefix);
        );
    System.out.println(Arrays.toString(listFiles));

【讨论】:

不清楚您的答案是否比已经提供和接受的其他答案更简单或更容易。并且“非凡的主张需要非凡的证据”。为什么这是简单易行的方法?您可以说 a 简单易行的方法,但仍应提供一个测试用例来显示您的代码正常工作。【参考方案2】:

仅使用 Java 流

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);

【讨论】:

【参考方案3】:

以下是由 Java 7 nio globbing 和 Java 8 lambda 提供支持的模式列出文件的示例:

    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));
    

【讨论】:

Files.walk(Paths.get("..")).filter(matcher::matches).forEach(System.out::println);【参考方案4】:

从Apache commons-io 尝试FileUtilslistFilesiterateFiles 方法):

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。

【讨论】:

好的,现在你已经知道 Jason S 发布问题时的确切位置了。 不完全。还有可以使用的 RegexFileFilter(但个人从来不需要这样做)。【参考方案5】:

从 Java 8 开始,您可以直接从 java.nio.file 使用 Files#find 方法。

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")
);

【讨论】:

您能否扩展示例以打印流中保存的第一个匹配项的路径?【参考方案6】:

使用方法:

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.txtfoo?bar.txt 匹配,这是不正确的 Jason 我使用了不包含路径的 file.getName()。 那么它不适用于我给出的示例模式:../Test?/sample*.txt【参考方案7】:

实现 JDK FileVisitor 接口。这是一个例子http://wilddiary.com/list-files-matching-a-naming-pattern-java/

【讨论】:

【参考方案8】:

Glob 的 Java7:Finding Files。 (Sample)

【讨论】:

【参考方案9】:

正如在另一个答案中发布的那样,通配符库适用于 glob 和正则表达式文件名匹配: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 方法(请参阅 Vladimir 的答案)来做到这一点,但没有成功(我现在意识到/认为它只能处理模式匹配一​​个目录或文件一次)。

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

当然,Java 7 可以很好地处理请求的功能,但不幸的是,我现在被 Java 6 困住了。该库相对较小,只有 13.5kb。

审稿人注意:我试图将上述内容添加到提及此库的现有答案中,但编辑被拒绝。我也没有足够的代表将此添加为评论。有没有更好的办法...

【讨论】:

您打算将您的项目迁移到其他地方吗?见code.google.com/p/support/wiki/ReadOnlyTransition 这不是我的项目,它看起来已经被迁移了:github.com/EsotericSoftware/wildcard【参考方案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 公共静态无效主要(字符串[]参数) 字符串 pathToScan = "."; 字符串目标文件; // fileThatYouWantToFilter 文件夹ToScan = 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); 
                
           
         

【讨论】:

【参考方案11】:

现在可能对您没有帮助,但 JDK 7 旨在将 glob 和正则表达式文件名匹配作为“更多 NIO 功能”的一部分。

【讨论】:

在 Java 7 中:Files.newDirectoryStream(path, glob-pattern)【参考方案12】:

您应该可以使用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,您不需要将* 替换为[.*]。我没有对此进行测试,但我确实经常使用模式和文件过滤器。

【讨论】:

【参考方案13】:

通配符库有效地进行 glob 和正则表达式文件名匹配:

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

实现很简洁——JAR 只有 12.9 KB。

【讨论】:

唯一的缺点是不在Maven Central中 这是 OSS,继续放在 Maven Central 上。 :)【参考方案14】:

考虑使用 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)。

【讨论】:

太棒了!顺便说一句,如果您需要目录,scanner.getIncludedDirectories() 也会这样做。 (getIncludedFiles 不起作用) github 上的通配符项目也很有魅力:github.com/EsotericSoftware/wildcard @Moreaki 属于单独的答案,而不是评论 在plexus-utils (241Kb) 中可以找到完全相同的DirectoryScanner。小于ant.jar (1.9Mb)。 这行得通。但与具有相同文件模式的 ls 相比,它似乎非常慢(使用 ls &lt;pattern&gt; 时的毫秒数与使用 DirectoryScanner 时的数分钟)...【参考方案15】:

您可以将通配符字符串转换为正则表达式,并将其与 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));

【讨论】:

这不适用于包含特殊正则表达式字符(如 (、+ 或 $) 的文件 我使用了'String regex = "^" + s.replace("?", ".?").replace("", ".?") + "$"' (星号在我的评论中因为某种原因消失了......) 为什么用'.*替换*?? public static boolean isFileMatchTargetFilePattern(final File f, final String targetPattern) ` ` String regex = 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); 附录:“?”表示强制字符,因此应将其替换为. 而不是.?【参考方案16】:

为什么不使用做类似的事情:

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

// now you have a fully qualified path

这样您就不必担心相对路径,并且可以根据需要进行通配符。

【讨论】:

因为相对路径也可以有通配符。【参考方案17】:

Apache 过滤器是为迭代已知目录中的文件而构建的。要在目录中也允许使用通配符,您必须在“\”或“/”上拆分路径,并分别对每个部分进行过滤。

【讨论】:

这行得通。这有点烦人,但不是特别容易出问题。不过,我确实期待 JDK7 的 glob 匹配功能。

以上是关于如何在 Java 中查找与通配符字符串匹配的文件?的主要内容,如果未能解决你的问题,请参考以下文章

Ant风格路径匹配的通配符

linux文件名匹配——通配符使用

通配符(一般用来查找文件)

通配符(一般用来查找文件)

Linux的正则表达式

文件搜索查找与对比