javafile-ioniodirectorystream

List all Files from a Directory that match a File Mask (a.k.a Pattern or Glob)


I want to list all files in a directory and subdirectories within that directory that match a file mask.

For example "M:\SOURCE\*.doc" while SOURCE may look like this:

|-- SOURCE
|   |-- Folder1
|   |   |-- File1.doc
|   |   |-- File1.txt
|   |-- File2.doc
|   |-- File3.xml

Should return File1.doc and File2.doc.

Initially, I use a DirectoryStream, because that already makes some checks for the mask/glob syntax as well as being able to use it for filtering as this ISN'T just some regex but an actual file mask that a regular user finds easier to understand

Files.newDirectoryStream(path, mask);

The problem is a DirectoryStream only checks the immediate path directory that you provide and not it's subdirectories

THEN comes a "flattening" method with Files.walk which is in fact able to look through all of the subdirectories, problem is, it DOES NOT provide with the possibility to "filter" by a File Mask the same way that a DirectoryStream does

Files.walk(path, Integer.MAX_VALUE);

So I'm stuck, unable to combine the best of both methods here...


Solution

  • I think I might have solved my own question with the insight received here and other questions mentioning the PathMatcher object

    final PathMatcher maskMatcher = FileSystems.getDefault()
                      .getPathMatcher("glob:" + mask);
    
    final List<Path> matchedFiles = Files.walk(path)
                      .collect(Collectors.toList());
    
    final List<Path> filesToRemove = new ArrayList<>(matchedFiles.size());
    
    matchedFiles.forEach(foundPath -> {
                if (!maskMatcher.matches(foundPath.getFileName()) || Files.isDirectory(foundPath)) {
                  filesToRemove.add(foundPath);
                }
              });
    
     matchedFiles.removeAll(filesToRemove);
    

    So basically .getPathMatcher("glob:" + mask); is the same thing that the DirectoryStream was doing to filter the files

    All I have to do now after that is filtering the list of paths that I get with Files.walk by removing the elements that do not match my PathMatcher and are not of type File