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

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

../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 []参数)中的简单通配符,从而“节省”我的时间和麻烦... ... 我很高兴我没有混合使用非文件参数

299909 次浏览

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

为什么不使用这样的方法:

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


// now you have a fully qualified path

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

现在可能无法帮助您,但是 JDK 7的目的是使 globb 和 regex 文件名匹配成为“更多 NIO 特性”的一部分。

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

尝试 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

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

通配符库可以高效地完成 globb 和 regex 文件名匹配:

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

实现非常简洁—— JAR 只有12.9 KB。

你应该能够使用 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,则不需要用 [.*]替换 *。我还没有测试过这个,但是我经常使用模式和文件过滤器。

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

我创建了名为 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
Public static void main (String [] args){
字符串 pathToScan = “ .”;
字符串 target _ file;//fileThatYouWantToFilter
文件夹 ToScan = 新文件(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);
}
}
}
}

正如在另一个答案中提到的,通配符库同时适用于 globb 和 regex 文件名匹配: http://code.google.com/p/wildcard/

我使用以下代码来匹配 globb 模式,包括绝对和相对 * 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 的答案)来处理任意用户提供的绝对类型 globb 模式,而无需搜索整个文件系统,这将需要对提供的 globb 进行一些预处理,以确定最大的非正则表达式/globb 前缀。

当然,Java7可以很好地处理所请求的功能,但不幸的是,我现在只能使用 Java6。该库相对较小,大小为13.5 kb。

请审阅人员注意: 我试图将上面的内容添加到现有的提到这个库的答案中,但是编辑被拒绝了。我也没有足够的名声来添加这个作为评论。有没有更好的办法..。

实现 JDKFileVisitor 接口

下面是使用 Java 7 nio 球状的和 Java8 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));
}

方法:

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

因为 Java8可以直接从 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")
);

或者一个将项放入简单字符串集合的示例:

import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Stream;


final Collection<String> simpleStringCollection = new ArrayList<>();
        

String wildCardValue = "*.txt";


final Path dir = Paths.get(".");


try {
Stream<Path> results = Files.find(dir,
Integer.MAX_VALUE,
(path, basicFileAttributes) -> path.toFile().getName().matches(wildCardValue)
);


results.forEach(p -> simpleStringCollection.add(p.toString()));
} catch (IOException e) {
throw new UncheckedIOException(e);
}

仅使用 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);

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