讀古今文學網 > Java程序員修煉之道 > 2.3 處理目錄和目錄樹 >

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\");//1設定起始路徑

try(DirectoryStream<Path> stream
      = Files.newDirectoryStream(dir, \"*.properties\")){//2聲明過濾流
  //3 找出所有.properties 文件並輸出
  for (Path entry: stream)
  {
      System.out.println(entry.getFileName);
  }
}
catch (IOException e)
{
  System.out.println(e.getMessage);
}
  

最前面是我們已經熟悉的Paths.get(String)調用1。緊隨其後的是關鍵方法DirectoryStream(Path directory, String patternMatch)2,它返回一個經過過濾的DirectoryStream,其中包含以.properties結尾的文件。最後輸出每個子項3。

過濾流中用到的模式匹配稱為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); //1調用walkFileTree
    }
    private static class FindJavaVisitor 
                         extends SimpleFileVisitor<Path> //2擴展SimpleFileVisitor<Path>
   {
      @Override
      public FileVisitResult 
        visitFile(Path file, BasicFileAttributes attrs) //3 重寫visitFile方法
     {
        if (file.toString.endsWith(\".java\")) {
           System.out.println(file.getFileName);
        }
        return FileVisitResult.CONTINUE;
     }
   }
}
  

整個過程從調用Files.walkFileTree方法開始1。這裡的關鍵是 FindJavaVisitor,該類擴展了FileVisitor的默認實現類SimpleFileVisitor2。你想讓SimpleFileVisitor來完成大部分工作,比如遍歷目錄。可實際上你唯一要做的就是重寫visitFile(Path,BasicFileAttributes)1方法3,在這個方法中你也只需要寫些代碼來判斷文件名是否以.java結尾,如果確實是,就在stdout中輸出。

1 2.4節會介紹BasicFileAttributes,所以暫時不用管它。

其他用例包括遞歸移動、複製、刪除或者修改文件。在大多數應用場景中,你只需要擴展SimpleFileVisitor。但如果你想實現自己的FileVisitor,API也很靈活。

注意 為了確保遞歸等操作的安全性,walkFileTree方法不會自動跟隨符號鏈接。如果你確實需要跟隨符號鏈接,就需要檢查那個屬性(如2.4.3節所述)並執行相應的操作。

現在你對路徑和目錄樹已經熟悉了,該從處理位置進入真正的文件系統操作上了,接下來我們會向你介紹新的Files類及它的朋友們。