Documentation

The Java™ Tutorials
Hide TOC
Finding Files查找文件
Trail: Essential Java Classes
Lesson: Basic I/O
Section: File I/O (Featuring NIO.2)

Finding Files查找文件

If you have ever used a shell script, you have most likely used pattern matching to locate files. 如果您曾经使用过shell脚本,那么您最有可能使用模式匹配来定位文件。In fact, you have probably used it extensively. 事实上,您可能已经广泛使用了它。If you haven't used it, pattern matching uses special characters to create a pattern and then file names can be compared against that pattern. 如果您没有使用它,模式匹配使用特殊字符来创建模式,然后可以将文件名与该模式进行比较。For example, in most shell scripts, the asterisk, *, matches any number of characters. 例如,在大多数shell脚本中,星号*匹配任意数量的字符。For example, the following command lists all the files in the current directory that end in .html:例如,以下命令列出当前目录中以.html结尾的所有文件:

% ls *.html

The java.nio.file package provides programmatic support for this useful feature. java.nio.file包为这个有用的特性提供了编程支持。Each file system implementation provides a PathMatcher. 每个文件系统实现都提供一个PathMatcherYou can retrieve a file system's PathMatcher by using the getPathMatcher(String) method in the FileSystem class. 您可以使用FileSystem类中的getPathMatcher(String)方法检索文件系统的PathMatcherThe following code snippet fetches the path matcher for the default file system:以下代码段获取默认文件系统的路径匹配器:

String pattern = ...;
PathMatcher matcher =
    FileSystems.getDefault().getPathMatcher("glob:" + pattern);

The string argument passed to getPathMatcher specifies the syntax flavor and the pattern to be matched. 传递给getPathMatcher的字符串参数指定要匹配的语法风格和模式。This example specifies glob syntax. 本例指定了glob语法。If you are unfamiliar with glob syntax, see What is a Glob.如果您不熟悉glob语法,请参阅什么是glob

Glob syntax is easy to use and flexible but, if you prefer, you can also use regular expressions, or regex, syntax. Glob语法易于使用且灵活,但如果您愿意,还可以使用正则表达式或正则表达式语法。For further information about regex, see the Regular Expressions lesson. 有关正则表达式的更多信息,请参阅正则表达式课程Some file system implementations might support other syntaxes.某些文件系统实现可能支持其他语法。

If you want to use some other form of string-based pattern matching, you can create your own PathMatcher class. 如果希望使用其他形式的基于字符串的模式匹配,可以创建自己的PathMatcher类。The examples in this page use glob syntax.本页中的示例使用glob语法。

Once you have created your PathMatcher instance, you are ready to match files against it. 创建PathMatcher实例后,就可以根据它匹配文件了。The PathMatcher interface has a single method, matches, that takes a Path argument and returns a boolean: It either matches the pattern, or it does not. PathMatcher接口有一个方法matches,它接受一个Path参数并返回一个布尔值:它要么匹配模式,要么不匹配。The following code snippet looks for files that end in .java or .class and prints those files to standard output:以下代码段查找以.java.class结尾的文件,并将这些文件打印到标准输出:

PathMatcher matcher =
    FileSystems.getDefault().getPathMatcher("glob:*.{java,class}");

Path filename = ...;
if (matcher.matches(filename)) {
    System.out.println(filename);
}

Recursive Pattern Matching递归模式匹配

Searching for files that match a particular pattern goes hand-in-hand with walking a file tree. 搜索与特定模式匹配的文件与遍历文件树密切相关。How many times do you know a file is somewhere on the file system, but where? Or perhaps you need to find all files in a file tree that have a particular file extension.有多少次你知道一个文件在文件系统的某个地方,但是在哪里?或者,您可能需要在文件树中查找具有特定文件扩展名的所有文件。

The Find example does precisely that. Find示例正是这样做的。Find is similar to the UNIX find utility, but has pared down functionally. Find类似于UNIX Find实用程序,但功能有所缩减。You can extend this example to include other functionality. 您可以扩展此示例以包括其他功能。For example, the find utility supports the -prune flag to exclude an entire subtree from the search. 例如,find实用程序支持-prune标志以从搜索中排除整个子树。You could implement that functionality by returning SKIP_SUBTREE in the preVisitDirectory method. 您可以通过在preVisitDirectory方法中返回SKIP_SUBTREE来实现该功能。To implement the -L option, which follows symbolic links, you could use the four-argument walkFileTree method and pass in the FOLLOW_LINKS enum (but make sure that you test for circular links in the visitFile method).要实现符号链接后面的-L选项,可以使用四参数walkFileTree方法并传入FOLLOW_LINKS枚举(但请确保在visitFile方法中测试循环链接)。

To run the Find application, use the following format:要运行查找应用程序,请使用以下格式:

% java Find <path> -name "<glob_pattern>"

The pattern is placed inside quotation marks so any wildcards are not interpreted by the shell. 模式放在引号内,因此shell不会解释任何通配符。For example:例如:

% java Find . -name "*.html"

Here is the source code for the Find example:以下是Find示例的源代码:

/**
 * Sample code that finds files that match the specified glob pattern.
 * For more information on what constitutes a glob pattern, see
 * https://docs.oracle.com/javase/tutorial/essential/io/fileOps.html#glob
 *
 * The file or directories that match the pattern are printed to
 * standard out.  The number of matches is also printed.
 *
 * When executing this application, you must put the glob pattern
 * in quotes, so the shell will not expand any wild cards:
 *              java Find . -name "*.java"
 */

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import static java.nio.file.FileVisitResult.*;
import static java.nio.file.FileVisitOption.*;
import java.util.*;


public class Find {

    public static class Finder
        extends SimpleFileVisitor<Path> {

        private final PathMatcher matcher;
        private int numMatches = 0;

        Finder(String pattern) {
            matcher = FileSystems.getDefault()
                    .getPathMatcher("glob:" + pattern);
        }

        // Compares the glob pattern against
        // the file or directory name.
        void find(Path file) {
            Path name = file.getFileName();
            if (name != null && matcher.matches(name)) {
                numMatches++;
                System.out.println(file);
            }
        }

        // Prints the total number of
        // matches to standard out.
        void done() {
            System.out.println("Matched: "
                + numMatches);
        }

        // Invoke the pattern matching
        // method on each file.
        @Override
        public FileVisitResult visitFile(Path file,
                BasicFileAttributes attrs) {
            find(file);
            return CONTINUE;
        }

        // Invoke the pattern matching
        // method on each directory.
        @Override
        public FileVisitResult preVisitDirectory(Path dir,
                BasicFileAttributes attrs) {
            find(dir);
            return CONTINUE;
        }

        @Override
        public FileVisitResult visitFileFailed(Path file,
                IOException exc) {
            System.err.println(exc);
            return CONTINUE;
        }
    }

    static void usage() {
        System.err.println("java Find <path>" +
            " -name \"<glob_pattern>\"");
        System.exit(-1);
    }

    public static void main(String[] args)
        throws IOException {

        if (args.length < 3 || !args[1].equals("-name"))
            usage();

        Path startingDir = Paths.get(args[0]);
        String pattern = args[2];

        Finder finder = new Finder(pattern);
        Files.walkFileTree(startingDir, finder);
        finder.done();
    }
}

Recursively walking a file tree is covered in Walking the File Tree.递归遍历文件树将在遍历文件树中介绍。


Previous page: Walking the File Tree
Next page: Watching a Directory for Changes