【问题标题】:How to find files that match a wildcard string in Java?如何在 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 进行详细的讨论/厌烦

标签: java file wildcard


【解决方案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 的答案)来做到这一点,但没有成功(我现在意识到/认为它只能处理模式匹配一​​个目录或文件一次)。

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

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

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

        【讨论】:

        【解决方案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 匹配功能。
            猜你喜欢
            • 2015-02-17
            • 2018-01-01
            • 1970-01-01
            • 2020-10-21
            • 1970-01-01
            • 2017-04-08
            • 1970-01-01
            • 2012-10-30
            • 2014-05-08
            相关资源
            最近更新 更多