java—列出目录中与文件掩码(又称模式或全局)匹配的所有文件

utugiqy6  于 2021-07-06  发布在  Java
关注(0)|答案(3)|浏览(298)

我想列出一个目录中的所有文件以及该目录中与文件掩码匹配的子目录。
例如“m:\source*.doc”,而source可能如下所示:

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

应该返回file1.doc和file2.doc。
最初,我使用directorystream,因为它已经对mask/glob语法进行了一些检查,并且能够使用它进行过滤,因为这不仅仅是一些regex,而是一个普通用户更容易理解的实际文件掩码

Files.newDirectoryStream(path, mask);

问题是directorystream只检查您提供的直接路径目录,而不检查它的子目录
然后出现了一个files.walk的“扁平化”方法,它实际上能够查看所有的子目录,问题是,它不能像directorystream那样通过文件掩码“过滤”

Files.walk(path, Integer.MAX_VALUE);

所以我被困住了,不能把这两种方法最好的结合起来。。。

57hvy0tb

57hvy0tb1#

我想我可能已经解决了我自己的问题与洞察力收到这里和其他问题提到 PathMatcher 对象

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

所以基本上 .getPathMatcher("glob:" + mask); 与directorystream过滤文件的操作相同
在那之后,我现在要做的就是过滤我从文件中得到的路径列表。通过删除与我的pathmatcher不匹配并且不是file类型的元素来遍历

bbmckpt7

bbmckpt72#

您还可以使用自定义 FileVisitor [1] ,结合 PathMatcher [2] ,与globs完美配合。
代码可能如下所示:

public static void main(String[] args) throws IOException {
    System.out.println(getFiles(Paths.get("/tmp/SOURCE"), "*.doc"));
}

public static List<Path> getFiles(final Path directory, final String glob) throws IOException {
    final var docFileVisitor = new GlobFileVisitor(glob);
    Files.walkFileTree(directory, docFileVisitor);

    return docFileVisitor.getMatchedFiles();
}

public static class GlobFileVisitor extends SimpleFileVisitor<Path> {

    private final PathMatcher pathMatcher;
    private List<Path> matchedFiles = new ArrayList<>();

    public GlobFileVisitor(final String glob) {
        this.pathMatcher = FileSystems.getDefault().getPathMatcher("glob:" + glob);
    }

    @Override
    public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException {
        if (pathMatcher.matches(path.getFileName())) {
            matchedFiles.add(path);
        }
        return FileVisitResult.CONTINUE;
    }

    public List<Path> getMatchedFiles() {
        return matchedFiles;
    }
}

[1] https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/filevisitor.html
[2] https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/pathmatcher.html

j0pj023g

j0pj023g3#

可以使用公共流 filter 从中检索筛选的文件名 Files.walk 使用 String::matches 使用适当的正则表达式:

final String SOURCE_DIR = "test";

Files.walk(Paths.get(SOURCE_DIR));
     .filter(p -> p.getFileName().toString().matches(".*\\.docx?"))
     .forEach(System.out::println);

输出

test\level01\level11\test.doc
test\level02\test-level2.doc
test\t1.doc
test\t3.docx

输入目录结构:

│   t1.doc
│   t2.txt
│   t3.docx
│   t4.bin
│
├───level01
│   │   test.do
│   │
│   └───level11
│           test.doc
│
└───level02
        test-level2.doc

更新
递归解决方案可以使用 newDirectoryStream 但是,它需要转换为流:

static Stream<Path> readFilesByMaskRecursively(Path start, String mask) {

    List<Stream<Path>> sub = new ArrayList<>();

    try {
        sub.add(StreamSupport.stream( // read files by mask in current dir
                Files.newDirectoryStream(start, mask).spliterator(), false));

        Files.newDirectoryStream(start, (path) -> path.toFile().isDirectory())
             .forEach(path -> sub.add(recursive(path, mask)));
    } catch (IOException ioex) {
        ioex.printStackTrace();
    }

    return sub.stream().flatMap(s -> s); // convert to Stream<Path>
}

// test
readFilesByMaskRecursively(Paths.get(SOURCE_DIR), "*.doc*")
             .forEach(System.out::println);

输出:

test\t1.doc
test\t3.docx
test\level01\level11\test.doc
test\level02\test-level2.doc

更新2
前缀 **/ 可以添加到 PathMatcher 跨越目录边界 Files.walk -基于Web的解决方案可以使用简化的筛选器,而无需删除特定条目:

String mask = "*.doc*";
PathMatcher maskMatcher = FileSystems.getDefault().getPathMatcher("glob:**/" + mask);
Files.walk(Paths.get(SOURCE_DIR))
     .filter(path -> maskMatcher.matches(path))
     .forEach(System.out::println);

输出(与递归解决方案相同):

test\level01\level11\test.doc
test\level02\test-level2.doc
test\t1.doc
test\t3.docx

相关问题