Java 文件树

Java IO教程 - Java文件树


FileVisitor API可以递归地处理文件树中的所有文件和目录。

当我们要对文件树中的所有或某些文件或目录执行某些操作时,FileVisitor API非常有用。

SimpleFileVisitor类是FileVisitor接口的基本实现。

当访问文件/目录时,SimpleFileVisitor类不执行任何操作。我们可以从SimpleFileVisitor类继承我们的文件访问类,并且只覆盖适合我们需要的方法。

FileVisitor接口的方法:

ID 含义
1 FileVisitResult preVisitDirectory(T dir,BasicFileAttributes attrs) 在访问目录中的条目之前调用一次。
2 FileVisitResult postVisitDirectory(T dir,IOException exc) 已访问目录中的后调用项。如果在目录的迭代期间抛出了任何异常,则将异常对象作为第二个参数传递给此方法。如果此方法的第二个参数为null,则在目录迭代期间没有异常。
3 FileVisitResult visitFile(T文件,BasicFileAttributes attrs) 当访问目录中的文件时调用。
4 FileVisitResult visitFileFailed(T文件,IOException exc) 当由于任何原因而无法访问文件或目录时调用。

下表列出了FileVisitResult的枚举常量及其说明

枚举常量 描述
CONTINUE 继续处理
SKIP_SIBLINGS 继续处理而不访问文件或目录的兄弟节点。
SKIP_SUBTREE 继续处理,而不访问目录中的条目。
TERMINATE 终止文件访问过程。

我们不需要在我们的文件访问类的所有四个方法中编写逻辑。要复制目录,请使用preVisitDirectory()方法来创建一个新目录和visitFile()方法来复制文件。

以下代码显示如何打印目录的子目录和文件的名称。

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

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class Main {
  public static void main(String[] args) {
    Path startDir = Paths.get("");
    FileVisitor<Path> visitor = getFileVisitor();
    try {
      Files.walkFileTree(startDir, visitor);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  public static FileVisitor<Path> getFileVisitor() {
    class DirVisitor<Path> extends SimpleFileVisitor<Path> {
      @Override
      public FileVisitResult preVisitDirectory(Path dir,
          BasicFileAttributes attrs) {

        System.out.format("%s [Directory]%n", dir);
        return CONTINUE;
      }

      @Override
      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
        System.out.format("%s [File,  Size: %s  bytes]%n", file, attrs.size());
        return CONTINUE;
      }
    }
    FileVisitor<Path> visitor = new DirVisitor<>();
    return visitor;
  }
}

上面的代码生成以下结果。

例子

以下代码显示如何使用FileVisitor API删除目录树。

import static java.nio.file.FileVisitResult.CONTINUE;
import static java.nio.file.FileVisitResult.TERMINATE;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class Main {
  public static void main(String[] args) {
    Path dirToDelete = Paths.get("DIR");
    FileVisitor<Path> visitor = getFileVisitor();

    try {
      Files.walkFileTree(dirToDelete, visitor);
    }
    catch (IOException e) {
      System.out.println(e.getMessage());
    }
  }

  public static FileVisitor<Path> getFileVisitor() {

    class DeleteDirVisitor extends SimpleFileVisitor<Path> {
      @Override
      public FileVisitResult postVisitDirectory(Path dir, IOException e)
          throws IOException {
        FileVisitResult result = CONTINUE;
        if (e != null) {
          System.out.format("Error deleting  %s.  %s%n", dir, e.getMessage());
          result = TERMINATE;
        } else {
          Files.delete(dir);
          System.out.format("Deleted directory  %s%n", dir);
        }
        return result;
      }

      @Override
      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
          throws IOException {
        Files.delete(file);
        System.out.format("Deleted file %s%n", file);
        return CONTINUE;
      }
    }
    FileVisitor<Path> visitor = new DeleteDirVisitor();
    return visitor;
  }
}

上面的代码生成以下结果。


例2

以下代码显示如何使用walkFileTree()方法跟随符号链接。

import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumSet;
import java.util.Set;
import static  java.nio.file.FileVisitOption.FOLLOW_LINKS;
public class Main {
  public static void main(String[] args) throws Exception {
    Path startDir = Paths.get("");
    FileVisitor<Path> visitor = create your visitor;

    Set<FileVisitOption> options = EnumSet.of(FOLLOW_LINKS);

    int depth = Integer.MAX_VALUE;

    Files.walkFileTree(startDir, options, depth, visitor);
  }
}

模式匹配

我们可以使用glob和正则表达式模式对字符串形式的Path对象执行模式匹配。

功能接口PathMatcher用于执行匹配。它包含一个方法matches(Path path)方法,如果指定的路径匹配模式,则该方法返回true。

模式字符串由两部分组成,语法和模式由冒号分隔:

syntax:pattern

语法的值是glob或regex。模式部分遵循取决于语法部分的值的语法。

glob模式使用以下语法规则:

  • * 匹配零个或多个字符,而不会交叉目录边界。
  • ** 匹配零个或多个字符跨目录边界。
  • ? 只匹配一个字符。
  • \ 转义以下字符的特殊含义。
  • \\ 匹配单个反斜杠。
  • \* 匹配星号。

放在括号 [] 中的字符称为括号表达式,它匹配单字符。[aeiou]匹配a,e,i,o或u。

两个字符之间的破折号指定范围。[a-z]匹配a和z之间的所有字母。

左括号后的感叹号(!)被视为否定。[!abc]匹配除a,b和c之外的所有字符。

通过在大括号({})中指定逗号分隔的子模式来使用一组子模式。例如,{txt,java,doc}匹配txt,java和doc。

路径的根组件的匹配是实现相关的。

以下代码显示了如何使用PathMatcher对象将路径与glob模式匹配。

import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) {
    String globPattern = "glob:**txt";
    PathMatcher matcher = FileSystems.getDefault().getPathMatcher(globPattern);
    Path path = Paths.get("C:\\Java_Dev\\test1.txt");
    boolean matched = matcher.matches(path);
    System.out.format("%s matches  %s:  %b%n", globPattern, path, matched);
  }
}

上面的代码生成以下结果。