如何在 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 尝试FileUtils
(listFiles
和iterateFiles
方法):
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.txt
与foo?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 <pattern>
时的毫秒数与使用 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 中查找与通配符字符串匹配的文件?的主要内容,如果未能解决你的问题,请参考以下文章