首页 » Java程序员修炼之道 » Java程序员修炼之道全文在线阅读

《Java程序员修炼之道》2.3 处理目录和目录树

关灯直达底部

在读2.2节关于路径的内容时,你可能已经猜到目录不过是带有特别属性的Path。遍历目录的能力是Java 7引人注目的新特性。新加入的java.nio.file.DirectoryStream<T>接口和它的实现类提供了很多功能:

  • 循环遍历目录中的子项,比如查找目录中的文件;
  • 用glob表达式1(比如*Foobar*)进行目录子项匹配和基于MIME的内容检测(比如text/xml文件);
  • walkFileTree方法实现递归移动、复制和删除操作。

本节主要讨论两个常见用例:在一个目录中查找文件以及在目录树中执行相同的任务。我们先从最简单的开始:在一个目录中查找任意文件。

1 glob通常指有限的模式匹配,源自早期Unix中的glob库函数,该函数用于查找文件系统中指定模式的路径,虽然所用语法和正则表达式类似,但没有正则表达式那么强大的表达力,详见附录B。——译者注

2.3.1 在目录中查找文件

我们先讨论一个简单的例子,用模式匹配过滤出java7developer项目中所有的.properties文件。请看下面的代码:

代码清单2-2 列出目录下的properties文件

Path dir = Paths.get(/"C://workspace//java7developer/");//①设定起始路径try(DirectoryStream<Path> stream      = Files.newDirectoryStream(dir, /"*.properties/")){//②声明过滤流  //③ 找出所有.properties 文件并输出  for (Path entry: stream)  {      System.out.println(entry.getFileName);  }}catch (IOException e){  System.out.println(e.getMessage);}  

最前面是我们已经熟悉的Paths.get(String)调用①。紧随其后的是关键方法DirectoryStream(Path directory, String patternMatch)②,它返回一个经过过滤的DirectoryStream,其中包含以.properties结尾的文件。最后输出每个子项③。

过滤流中用到的模式匹配称为glob模式匹配,它和Perl正则表达式类似,但稍有不同。附录B中有如何使用glob模式匹配的详细说明。

代码清单2-2展示了新API处理单个目录的能力,但如果需要递归过滤多个目录时该怎么办?

2.3.2 遍历目录树

Java 7支持整个目录树的遍历。也就是说你可以很容易地搜寻目录树中的文件,在子目录中查找,并对它们执行操作。比如你可能想在做开发的机器上弄个工具类来删除目录/opt/workspace/java下的所有.class文件,完成构建前的清除工作。

遍历目录树是Java 7的新特性,要想正确使用,你得掌握一些接口及其实现的细节。其中的关键方法是:

Files.walkFileTree(Path startingDir, FileVisitor<? superPath> visitor);  

提供startingDir非常简单,但给出FileVisitor接口的实现类就比较麻烦了(参数FileVisitor<? superPath> visitor看上去就不是善茬儿),因为最少得实现下面5个方法(T一般就是Path):

  • FileVisitResult preVisitDirectory(T dir)
  • FileVisitResult preVisitDirectoryFailed(T dir, IOException exc)
  • FileVisitResult visitFile(T file, BasicFileAttributes attrs)
  • FileVisitResult visitFileFailed(T file, IOException exc)
  • FileVisitResult postVisitDirectory(T dir, IOException exc)

看起来挺吓人的吧?好在Java 7 API的设计者们已经提供了一个默认实现类,SimpleFileVisitor<T>

我们要扩展并修改代码清单2-2。下面的代码会列出C:workspacejava7developersrc目录下及其子目录内的所有.java文件。这段代码展示了Files.walkFileTree方法对默认实现类SimpleFileVisitor的用法,用一个特定的visitFile方法实现来改进它。

代码清单2-3 列出子目录下的所有java源码文件

public class Find{  public static void main(String args) throws IOException  {    Path startingDir =       Paths.get(/"C://workspace//java7developer//src/");//设置起始目录    Files.walkFileTree(startingDir,                        new FindJavaVisitor); //①调用walkFileTree    }    private static class FindJavaVisitor                          extends SimpleFileVisitor<Path> //②扩展SimpleFileVisitor<Path>   {      @Override      public FileVisitResult         visitFile(Path file, BasicFileAttributes attrs) //③ 重写visitFile方法     {        if (file.toString.endsWith(/".java/")) {           System.out.println(file.getFileName);        }        return FileVisitResult.CONTINUE;     }   }}  

整个过程从调用Files.walkFileTree方法开始①。这里的关键是 FindJavaVisitor,该类扩展了FileVisitor的默认实现类SimpleFileVisitor②。你想让SimpleFileVisitor来完成大部分工作,比如遍历目录。可实际上你唯一要做的就是重写visitFile(Path,BasicFileAttributes)1方法③,在这个方法中你也只需要写些代码来判断文件名是否以.java结尾,如果确实是,就在stdout中输出。

1 2.4节会介绍BasicFileAttributes,所以暂时不用管它。

其他用例包括递归移动、复制、删除或者修改文件。在大多数应用场景中,你只需要扩展SimpleFileVisitor。但如果你想实现自己的FileVisitor,API也很灵活。

注意为了确保递归等操作的安全性,walkFileTree方法不会自动跟随符号链接。如果你确实需要跟随符号链接,就需要检查那个属性(如2.4.3节所述)并执行相应的操作。

现在你对路径和目录树已经熟悉了,该从处理位置进入真正的文件系统操作上了,接下来我们会向你介绍新的Files类及它的朋友们。