Reputation: 189886
This should be really simple. If I have a String like this:
../Test?/sample*.txt
then what is a generally-accepted way to get a list of files that match this pattern? (e.g. it should match ../Test1/sample22b.txt
and ../Test4/sample-spiffy.txt
but not ../Test3/sample2.blah
or ../Test44/sample2.txt
)
I've taken a look at org.apache.commons.io.filefilter.WildcardFileFilter
and it seems like the right beast but I'm not sure how to use it for finding files in a relative directory path.
I suppose I can look the source for ant since it uses wildcard syntax, but I must be missing something pretty obvious here.
(edit: the above example was just a sample case. I'm looking for the way to parse general paths containing wildcards at runtime. I figured out how to do it based on mmyers' suggestion but it's kind of annoying. Not to mention that the java JRE seems to auto-parse simple wildcards in the main(String[] arguments) from a single argument to "save" me time and hassle... I'm just glad I didn't have non-file arguments in the mix.)
Upvotes: 196
Views: 325826
Reputation: 1411
I was trying to solve similar issue with Kotlin.
I need to get a file with path like:
/a/b/c/file
The problem is: folder c
can have different names.
I ended up with this method. It combines two suggested answers (one, two):
private fun findFirstMatchedFileByWildcardPath1(wildcardPath: Path): Path {
val pattern = wildcardPath.toString()
val simpleStringCollection: MutableCollection<String> = ArrayList()
val dir = Paths.get("/some/known/root/folder")
try {
val results: Stream<Path> = Files.find(dir, Int.MAX_VALUE,
{ path: Path, _: BasicFileAttributes? ->
val pathMatcher: PathMatcher = FileSystems.getDefault().getPathMatcher("glob:$pattern")
pathMatcher.matches(path)
}
)
results.forEach { p -> simpleStringCollection.add(p.toString()) }
} catch (e: IOException) {
throw UncheckedIOException(e)
}
val firstMatchedPath = simpleStringCollection.first()
return Path.of(firstMatchedPath)
}
For me it's enough to return the first match, but it can be easily changed.
I hope this will help somebody!
Upvotes: 0
Reputation: 26210
Here are examples of listing files by pattern powered by Java 7 nio globbing (glob format) and Java 8 lambdas:
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(
Paths.get(".."), "Test?/sample*.txt")) {
dirStream.forEach(path -> System.out.println(path));
}
or
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));
}
Upvotes: 76
Reputation: 6205
Using Java streams only
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);
Upvotes: 1
Reputation: 2363
Since Java 8 you can use Files#find
method directly from 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")
);
Or an example of putting items in a simple string collection:
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);
}
Upvotes: 40
Reputation: 1
The most simple and easy way by using the io library's File class would be :
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));
Upvotes: -1
Reputation: 6871
Try FileUtils
from Apache commons-io (listFiles
and iterateFiles
methods):
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]);
}
To solve your issue with the TestX
folders, I would first iterate through the list of folders:
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"));
}
}
Quite a 'brute force' solution but should work fine. If this doesn't fit your needs, you can always use the RegexFileFilter.
Upvotes: 150
Reputation: 1528
Util Method:
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:
@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));
}
}
}
Output:
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
Upvotes: 0
Reputation: 7234
Implement the JDK FileVisitor interface. Here is an example http://wilddiary.com/list-files-matching-a-naming-pattern-java/
Upvotes: 0
Reputation: 794
As posted in another answer, the wildcard library works for both glob and regex filename matching: http://code.google.com/p/wildcard/
I used the following code to match glob patterns including absolute and relative on *nix style file systems:
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();
I spent some time trying to get the FileUtils.listFiles methods in the Apache commons io library (see Vladimir's answer) to do this but had no success (I realise now/think it can only handle pattern matching one directory or file at a time).
Additionally, using regex filters (see Fabian's answer) for processing arbitrary user supplied absolute type glob patterns without searching the entire file system would require some preprocessing of the supplied glob to determine the largest non-regex/glob prefix.
Of course, Java 7 may handle the requested functionality nicely, but unfortunately I'm stuck with Java 6 for now. The library is relatively minuscule at 13.5kb in size.
Note to the reviewers: I attempted to add the above to the existing answer mentioning this library but the edit was rejected. I don't have enough rep to add this as a comment either. Isn't there a better way...
Upvotes: 6
Reputation: 1528
Simple Way without using any external import is to use this method
I created csv files named with billing_201208.csv ,billing_201209.csv ,billing_201210.csv and it looks like working fine.
Output will be the following if files listed above exists
found billing_201208.csv
found billing_201209.csv
found billing_201210.csv
//Use Import ->import java.io.File public static void main(String[] args) { String pathToScan = "."; String target_file ; // fileThatYouWantToFilter File folderToScan = 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); } } } }
Upvotes: 12
Reputation: 147164
Might not help you right now, but JDK 7 is intended to have glob and regex file name matching as part of "More NIO Features".
Upvotes: 18
Reputation: 51
You should be able to use the WildcardFileFilter
. Just use System.getProperty("user.dir")
to get the working directory. Try this:
public static void main(String[] args) {
File[] files = (new File(System.getProperty("user.dir"))).listFiles(new WildcardFileFilter(args));
//...
}
You should not need to replace *
with [.*]
, assuming wildcard filter uses java.regex.Pattern
. I have not tested this, but I do use patterns and file filters constantly.
Upvotes: 5
Reputation: 5876
The wildcard library efficiently does both glob and regex filename matching:
http://code.google.com/p/wildcard/
The implementation is succinct -- JAR is only 12.9 kilobytes.
Upvotes: 13
Reputation: 1972
Consider DirectoryScanner from Apache Ant:
DirectoryScanner scanner = new DirectoryScanner();
scanner.setIncludes(new String[]{"**/*.java"});
scanner.setBasedir("C:/Temp");
scanner.setCaseSensitive(false);
scanner.scan();
String[] files = scanner.getIncludedFiles();
You'll need to reference ant.jar (~ 1.3 MB for ant 1.7.1).
Upvotes: 93
Reputation: 45754
You could convert your wildcard string to a regular expression and use that with String's matches
method. Following your example:
String original = "../Test?/sample*.txt";
String regex = original.replace("?", ".?").replace("*", ".*?");
This works for your examples:
Assert.assertTrue("../Test1/sample22b.txt".matches(regex));
Assert.assertTrue("../Test4/sample-spiffy.txt".matches(regex));
And counter-examples:
Assert.assertTrue(!"../Test3/sample2.blah".matches(regex));
Assert.assertTrue(!"../Test44/sample2.txt".matches(regex));
Upvotes: 30
Reputation: 13604
Why not use do something like:
File myRelativeDir = new File("../../foo");
String fullPath = myRelativeDir.getCanonicalPath();
Sting wildCard = fullPath + File.separator + "*.txt";
// now you have a fully qualified path
Then you won't have to worry about relative paths and can do your wildcarding as needed.
Upvotes: 0
Reputation: 192055
The Apache filter is built for iterating files in a known directory. To allow wildcards in the directory also, you would have to split the path on '\
' or '/
' and do a filter on each part separately.
Upvotes: 3