Documentation

The Java™ Tutorials
Hide TOC
Walking the File Tree遍历文件树
Trail: Essential Java Classes
Lesson: Basic I/O
Section: File I/O (Featuring NIO.2)

Walking the File Tree遍历文件树

Do you need to create an application that will recursively visit all the files in a file tree? 您是否需要创建一个递归访问文件树中所有文件的应用程序?Perhaps you need to delete every .class file in a tree, or find every file that hasn't been accessed in the last year. 也许您需要删除树中的每个.class文件,或者查找去年未访问过的每个文件。You can do so with the FileVisitor interface.您可以使用FileVisitor界面执行此操作。

This section covers the following:本节包括以下内容:

The FileVisitor InterfaceFileVisitor界面

To walk a file tree, you first need to implement a FileVisitor. 要遍历文件树,首先需要实现FileVisitorA FileVisitor specifies the required behavior at key points in the traversal process: when a file is visited, before a directory is accessed, after a directory is accessed, or when a failure occurs. FileVisitor在遍历过程中的关键点指定所需的行为:访问文件时、访问目录前、访问目录后或发生故障时。The interface has four methods that correspond to these situations:该接口有四种对应于这些情况的方法:

If you don't need to implement all four of the FileVisitor methods, instead of implementing the FileVisitor interface, you can extend the SimpleFileVisitor class. 如果不需要实现所有四个FileVisitor方法,而不是实现FileVisitor接口,那么可以扩展SimpleFileVisitor类。This class, which implements the FileVisitor interface, visits all files in a tree and throws an IOError when an error is encountered. 此类实现FileVisitor接口,访问树中的所有文件,并在遇到错误时抛出IOErrorYou can extend this class and override only the methods that you require.可以扩展此类并仅重写所需的方法。

Here is an example that extends SimpleFileVisitor to print all entries in a file tree. 下面是一个扩展SimpleFileVisitor以打印文件树中所有条目的示例。It prints the entry whether the entry is a regular file, a symbolic link, a directory, or some other "unspecified" type of file. 无论条目是常规文件、符号链接、目录还是其他“未指定”类型的文件,它都会打印条目。It also prints the size, in bytes, of each file. Any exception that is encountered is printed to the console.它还打印每个文件的大小(以字节为单位)。遇到的任何异常都会打印到控制台。

The FileVisitor methods are shown in bold:FileVisitor方法以粗体显示:

import static java.nio.file.FileVisitResult.*;

public static class PrintFiles
    extends SimpleFileVisitor<Path> {

    // Print information about
    // each type of file.
    @Override
    public FileVisitResult visitFile(Path file,
                                   BasicFileAttributes attr) {
        if (attr.isSymbolicLink()) {
            System.out.format("Symbolic link: %s ", file);
        } else if (attr.isRegularFile()) {
            System.out.format("Regular file: %s ", file);
        } else {
            System.out.format("Other: %s ", file);
        }
        System.out.println("(" + attr.size() + "bytes)");
        return CONTINUE;
    }

    // Print each directory visited.
    @Override
    public FileVisitResult postVisitDirectory(Path dir,
                                          IOException exc) {
        System.out.format("Directory: %s%n", dir);
        return CONTINUE;
    }

    // If there is some error accessing
    // the file, let the user know.
    // If you don't override this method
    // and an error occurs, an IOException 
    // is thrown.
    @Override
    public FileVisitResult visitFileFailed(Path file,
                                       IOException exc) {
        System.err.println(exc);
        return CONTINUE;
    }
}

Kickstarting the Process启动这一进程

Once you have implemented your FileVisitor, how do you initiate the file walk? 一旦实现了FileVisitor,如何启动文件漫游?There are two walkFileTree methods in the Files class.Files类中有两个walkFileTree方法。

The first method requires only a starting point and an instance of your FileVisitor. 第一种方法只需要FileVisitor的起点和实例。You can invoke the PrintFiles file visitor as follows:您可以按如下方式调用PrintFiles文件访问者:

Path startingDir = ...;
PrintFiles pf = new PrintFiles();
Files.walkFileTree(startingDir, pf);

The second walkFileTree method enables you to additionally specify a limit on the number of levels visited and a set of FileVisitOption enums. 第二个walkFileTree方法使您能够另外指定访问的级别数限制和一组FileVisitOption枚举。If you want to ensure that this method walks the entire file tree, you can specify Integer.MAX_VALUE for the maximum depth argument.如果要确保此方法遍历整个文件树,可以为“最大深度”参数指定Integer.MAX_VALUE

You can specify the FileVisitOption enum, FOLLOW_LINKS, which indicates that symbolic links should be followed.您可以指定FileVisition枚举,FOLLOW_LINKS,这表示应该遵循符号链接。

This code snippet shows how the four-argument method can be invoked:此代码段显示了如何调用四参数方法:

import static java.nio.file.FileVisitResult.*;

Path startingDir = ...;

EnumSet<FileVisitOption> opts = EnumSet.of(FOLLOW_LINKS);

Finder finder = new Finder(pattern);
Files.walkFileTree(startingDir, opts, Integer.MAX_VALUE, finder);

Considerations When Creating a FileVisitor创建FileVisitor时的注意事项

A file tree is walked depth first, but you cannot make any assumptions about the iteration order that subdirectories are visited.文件树是深度优先的,但不能对访问子目录的迭代顺序做出任何假设。

If your program will be changing the file system, you need to carefully consider how you implement your FileVisitor.如果您的程序将更改文件系统,则需要仔细考虑如何实现FileVisitor

For example, if you are writing a recursive delete, you first delete the files in a directory before deleting the directory itself. 例如,如果要编写递归删除,则首先删除目录中的文件,然后再删除目录本身。In this case, you delete the directory in postVisitDirectory.在本例中,您将删除postVisitDirectory中的目录。

If you are writing a recursive copy, you create the new directory in preVisitDirectory before attempting to copy the files to it (in visitFiles). 如果要编写递归副本,请在尝试将文件复制到preVisitDirectory(在visitFiles中)之前,在preVisitDirectory中创建新目录。If you want to preserve the attributes of the source directory (similar to the UNIX cp -p command), you need to do that after the files have been copied, in postVisitDirectory. 如果希望保留源目录的属性(类似于UNIX cp -p命令),则需要在复制文件后在postVisitDirectory中执行此操作。The Copy example shows how to do this.Copy示例演示了如何执行此操作。

If you are writing a file search, you perform the comparison in the visitFile method. 如果要编写文件搜索,请在visitFile方法中执行比较。This method finds all the files that match your criteria, but it does not find the directories. 此方法查找所有符合条件的文件,但找不到目录。If you want to find both files and directories, you must also perform the comparison in either the preVisitDirectory or postVisitDirectory method. 如果要同时查找文件和目录,还必须在preVisitDirectorypostVisitDirectory方法中执行比较。The Find example shows how to do this.Find示例演示了如何执行此操作。

You need to decide whether you want symbolic links to be followed. 您需要决定是否要遵循符号链接。If you are deleting files, for example, following symbolic links might not be advisable. 例如,如果要删除文件,则不建议使用以下符号链接。If you are copying a file tree, you might want to allow it. 如果要复制文件树,可能需要允许它。By default, walkFileTree does not follow symbolic links.默认情况下,walkFileTree不跟随符号链接。

The visitFile method is invoked for files. 为文件调用visitFile方法。If you have specified the FOLLOW_LINKS option and your file tree has a circular link to a parent directory, the looping directory is reported in the visitFileFailed method with the FileSystemLoopException. 如果指定了FOLLOW_LINKS选项,并且文件树具有指向父目录的循环链接,则会在visitFileFailed方法中报告循环目录,并显示FileSystemLoopExceptionThe following code snippet shows how to catch a circular link and is from the Copy example:下面的代码片段显示了如何捕获循环链接,它来自Copy示例:

@Override
public FileVisitResult
    visitFileFailed(Path file,
        IOException exc) {
    if (exc instanceof FileSystemLoopException) {
        System.err.println("cycle detected: " + file);
    } else {
        System.err.format("Unable to copy:" + " %s: %s%n", file, exc);
    }
    return CONTINUE;
}

This case can occur only when the program is following symbolic links.只有当程序遵循符号链接时,才会发生这种情况。

Controlling the Flow控制流量

Perhaps you want to walk the file tree looking for a particular directory and, when found, you want the process to terminate. 也许您希望遍历文件树以查找特定目录,当找到该目录时,您希望进程终止。Perhaps you want to skip specific directories.也许您想跳过特定的目录。

The FileVisitor methods return a FileVisitResult value. FileVisitor方法返回FileVisitResult值。You can abort the file walking process or control whether a directory is visited by the values you return in the FileVisitor methods:您可以中止文件遍历过程或控制FileVisitor方法中返回的值是否访问目录:

In this code snippet, any directory named SCCS is skipped:在此代码段中,将跳过任何名为SCCS的目录:

import static java.nio.file.FileVisitResult.*;

public FileVisitResult
     preVisitDirectory(Path dir,
         BasicFileAttributes attrs) {
    (if (dir.getFileName().toString().equals("SCCS")) {
         return SKIP_SUBTREE;
    }
    return CONTINUE;
}

In this code snippet, as soon as a particular file is located, the file name is printed to standard output, and the file walking terminates:在此代码段中,一旦找到特定文件,文件名将打印到标准输出,文件遍历将终止:

import static java.nio.file.FileVisitResult.*;

// The file we are looking for.
Path lookingFor = ...;

public FileVisitResult
    visitFile(Path file,
        BasicFileAttributes attr) {
    if (file.getFileName().equals(lookingFor)) {
        System.out.println("Located file: " + file);
        return TERMINATE;
    }
    return CONTINUE;
}

Examples例子

The following examples demonstrate the file walking mechanism:以下示例演示了文件遍历机制:


Previous page: Links, Symbolic or Otherwise
Next page: Finding Files