Java NIO 在目录中搜索文件

5

我希望使用Java NIO和glob在特定的目录中查找文件(不知道完整的文件名)。

public static void match(String glob, String location) throws IOException {

        final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(
                glob);

        Files.walkFileTree(Paths.get(location), new SimpleFileVisitor<Path>() {

            @Override
            public FileVisitResult visitFile(Path path,
                    BasicFileAttributes attrs) throws IOException {
                if (pathMatcher.matches(path)) {
                    System.out.println(path);
                }
                return FileVisitResult.CONTINUE;
            }
        });
    }

在阅读一些教程后,我做了这个。如果我找到(第一个)与给定的全局字符串匹配的文件,我只想返回字符串。

if (pathMatcher.matches(path)) {
    return path.toString();
}
4个回答

7
有两件事情需要改变:
要“查找(第一个)具有给定glob字符串的文件”,如果遇到符合条件的文件,您需要完成遍历树,因此需要将匹配的路径作为结果存储。 Files.walkFileTree本身的结果是“起始文件”(JavaDoc),即指向locationPath
public static String match(String glob, String location) throws IOException {
    StringBuilder result = new StringBuilder();
    PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(glob);
    Files.walkFileTree(Paths.get(location), new SimpleFileVisitor<Path>() {

        @Override
        public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
            if (pathMatcher.matches(path)) {
                result.append(path.toString());
                return FileVisitResult.TERMINATE;
            }
            return FileVisitResult.CONTINUE;
        }
    });

    return result.toString();
}

如果没有匹配项,则结果中的String为空。
编辑:
使用Files.walk,我们可以使用基于模式匹配器的glob表达式实现更少的代码搜索:
public static Optional<Path> match(String glob, String location) throws IOException {
    PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(glob);
    return Files.walk(Paths.get(location)).filter(pathMatcher::matches).findFirst();
}

Optional 作为结果表明可能没有匹配。


使用可空的 String 而不是 StringBuilder。这样可以减少开销,仍然保持准确性。 - Olivier Grégoire
这应该怎么工作?String变量将不再是有效的最终变量。 - LuCio
你是对的。如果你使用Java 11+,可以尝试放置一个AtomicReference或使用var wrapper = new Object() { Path reference; } - Olivier Grégoire
你认为这样会更易读吗?这是因为你更喜欢在没有结果时返回null而不是空的String吗? - LuCio
或者 try(Stream<Path> stream = Files.find(Paths.get(location), Integer.MAX_VALUE, (path,attr) -> pathMatcher.matches(path))) { return stream.findFirst().orElse(null); } - Holger

4
基于您自己的代码。一旦找到匹配项,停止遍历。
public class Main {

    private static Path found = "nothing"; // First file found

    public static void main(String[] args) throws IOException {
        String glob = "glob:**.{java}"; //pattern to look for
        String location = "D:\\"; //where to search
        match(location, glob);
        System.out.println("Found: " + found);
    }

    public static void match(String location, String glob) throws IOException {

        final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(glob);

        Files.walkFileTree(Paths.get(location), new SimpleFileVisitor<Path>() {

            @Override
            public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
                if (pathMatcher.matches(path)) {
                    found = path; // Match found, stop traversal
                    return FileVisitResult.TERMINATE;
                }
                return FileVisitResult.CONTINUE;
            }
        });
    }
}

或者,您可以填充集合并保存与模式匹配的所有文件


1
我不确定您希望{prop*}做什么,但我建议避免将{alt1, alt2,...}语法与通配符混合使用。我刚在bash下测试了它,内部通配符没有被扩展,而是被理解为字面上的*。我还没有在Java中进行过测试,所以它可能有效,但您至少会让熟悉Linux的人感到困惑。 - Aaron
@Aaron 使用它在Windows中查找属性。不确定bash怎么样,但你是对的,最好进行编辑以避免潜在麻烦。 - Akceptor
此外,OP想要返回一个字符串。您能否将found放入match方法中,并在方法末尾返回它,使得这个原本返回void的方法变成了返回Path的方法? - Olivier Grégoire

1

我不知道这个例子是否能进一步帮助你,但似乎你需要自己的自定义文件访问器。以下是另一种解决方案:

package com.jesperancinha.files;

import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class GlobFileVisitor extends SimpleFileVisitor<Path> {
    private final PathMatcher pathMatcher;

    private Path path;

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

    @Override
    public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
        if (pathMatcher.matches(path)) {
            this.path = path;
            return FileVisitResult.TERMINATE;
        }
        return FileVisitResult.CONTINUE;
    }

    public Path getFoundPath() {
        return path;
    }
}

这是您运行示例的另一种可能性:

package com.jesperancinha.files;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FileFinder {
    public static void main(String[] args) throws IOException {
        String glob = "glob:**.{java}";
        String location = "/Users/joao/dev/src/jesperancinha";
        Path found = match(location, glob);
        System.out.println("Found: " + found);
    }

    public static Path match(String location, String glob) throws IOException {
        GlobFileVisitor globFileVisitor = new GlobFileVisitor(glob);
        Files.walkFileTree(Paths.get(location), globFileVisitor);
        return globFileVisitor.getFoundPath();
    }
}

0

这些答案都可以工作,但使用NIO搜索文件的预期方式是使用Files.FindBiPreicate<Path,BasicFileAttributes>

PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.txt");
Stream<Path> stream = Files.find(
        Paths.get("/dirToSearch"),
        1, // Search depth of 1 in our case, set to >1 or -1 to explore subfolders
        (path, basicFileAttributes) ->  matcher.matches(path)
);
// The stream is lazily populated
Path firstMatch = stream.findFirst().get();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接