天天看點

Java SE 7 新特性之檔案操作(2)- Path類的基本操作

轉自開發者的天空

在上面的一篇文章中,我們已經說過了Path 類的操作主要有兩種:對路徑的操作和對檔案的操 作。這篇文章中我們就來了解一下對路徑的操作。

建立Path執行個體

Path執行個體包含了指定檔案或目錄位置的資訊,在執行個體化Path類時,需要指定一個或多個目錄或檔案名。路徑的根目錄不是必須的;路徑資訊可能僅僅是一個 目錄或檔案的名稱。

最簡單的建立Path執行個體的方式就是使用Paths(注意這裡有一個s)類的get方法:

  1.         Path p1 = Paths.get("/tmp/foo");
  2.         Path p2 = Paths.get(args[0]);
  3.         Path p3 = Paths.get("file:///Users/joe/FileTest.java");

複制代碼

Path類接受String或URI作為參數。

擷取路徑資訊

前面我們已經說過了,File System一般是樹形結構,是以我們可以把Path了解為按順序存儲的一系列的名稱(目錄名稱和檔案名稱)。目錄結構中最高一層的目錄名就是序列中 index為0的那一個,目錄結構中最低一層的目錄名或者檔案名就是序列中index為n-1的那一個(這裡n是路徑中層次的數目)。Path類提供方法 來通過index擷取序列中的一個元素或一個子序列。

随後的例子中我們使用的目錄結構如下圖:

Java SE 7 新特性之檔案操作(2)- Path類的基本操作
Java SE 7 新特性之檔案操作(2)- Path類的基本操作

下載下傳 (18.96 KB)

2010-3-17 14:57

    下面的代碼定義了一個Path對象并擷取其中的資訊。要注意的是這些代碼中除了isHidden方法外,其他的方法并不需要指定的目錄或檔案存在;如果不 存在,isHidden方法會抛出異常。

  1.         Path path = Paths.get("C:\\home\\joe\\foo");    // Microsoft Windows syntax
  2.         //Path path = Paths.get("/home/joe/foo");    // Solaris syntax
  3.         System.out.format("toString: %s%n", path.toString());
  4.         System.out.format("getName: %s%n", path.getName());
  5.         System.out.format("getName(0): %s%n", path.getName(0));
  6.         System.out.format("getNameCount: %d%n", path.getNameCount());
  7.         System.out.format("subpath(0,2): %d%n", path.subpath(0,2));
  8.         System.out.format("getParent: %s%n", path.getParent());
  9.         System.out.format("getRoot: %s%n", path.getRoot());
  10.         System.out.format("isHidden: %s%n", path.isHidden());

複制代碼

下面是這段代碼的輸出情況

方法 Solaris下的輸出 Windows下的 輸出 備注
toString /home/joe/foo C:\home\joe\foo
getName foo foo 獲 取名稱序列中的最後一個,也就是最底層的目錄或檔案名
getName(0) home home 獲 取名稱序列中的第一個,也就是最靠近根目錄的那一層。注意根目錄不在名稱序列中
getNameCount 3 3 獲 取名稱序列的元素個數
subpath(0,2) home/joe home\joe 獲 取從指定的開始點到指定的結束點的子路徑。注意這裡包括開始點,但不包括結束點。
getParent /home/joe \home\joe 返 回Path指定的目錄或檔案的父目錄
getRoot / C:\ 返 回根目錄
isHidden false false 如果檔案是 隐藏檔案,或者目錄是隐藏目錄,傳回true。因為要通路檔案的屬性,是以如果Path指定的目錄或者檔案不存在,會抛出異常。

上面的代碼中我們建立Path時使用的是絕對路徑,下面我們來看看建立路徑時使用相對路徑時,這段代碼的執行結果:

  1.         //Path path = Paths.get("sally/bar");     // Solaris syntax
  2.         Path path = Paths.get("sally\\bar");    // Microsoft Windows syntax

複制代碼

大 家可以自行去實驗一下具體的輸出是什麼。

去除Path中的備援

在很多檔案系統中我們使用'.'來代表目前目錄,使用'..'代表父目錄。在有些情況下我們建立的路徑中會有備援的路徑資訊,例如:

        /home/./joe/foo

        /home/sally/../joe/foo

方法normalize會去除這些備援資訊,包括'.'或'directory/..'。上面的兩個例子在去除備援資訊後都是/home/joe /foo。

要注意的是normalize方法并不去檢查檔案系統,它隻是簡單的進行文法操作。在第二個例子中,如果sally是一個指向其他的目錄的符号連結,那麼 去除了sally/..後可能導緻Path不在指向原來的檔案或目錄。

如果你需要清除備援資訊,又要保證結果仍然指向正确的檔案或目錄,可以使用toRealPath方法。在下面我們會講到這個方法。

轉換Path

有3個方法用來轉換Path。

  • toUri方法

    如果你需要将Path轉換為可以在浏覽器中打開的字元串格式,可以使用toUri方法,例如:

    1.         Path p1 = Paths.get("/home/logfile");
    2.         System.out.format("%s%n", p1.toUri());  // 結果是 file:///home/logfile
    複 制代碼 注意在這裡即使/home/logfile'指向的目錄或檔案不存在,這段代碼同樣能夠執行成功。
  • toAbsolutePath 方法

    該方法将路徑轉換為絕對路徑。如果原來的Path已經是絕對路徑,該方法直接傳回原有的Path對象。

    我們來看看下面的例子:

    1.                 Path path = Paths.get("home\\joe\\foo");
    2.                 Path absolutePath = path.toAbsolutePath();
    3.                 System.out.println(path == absolutePath); //結果是false
    4.                 Path path2 = Paths.get("c:\\home\\joe\\foo ");
    5.                 Path absolutePath2 = path2.toAbsolutePath();
    6.                 System.out.println(path2 == absolutePath2);//結果是true
    複制代碼 同樣的,toAbsolutePath方法并不需要 Path所指向的檔案或目錄存在。
  • toRealPath方法

    這個方法會傳回一個已經存在的檔案或目錄的真實路徑(如果檔案或目錄不存在或無法通路,該方法會抛出異常)。該方法會執行以下的操作:

    如果傳入的參數是true并且檔案系統支援符号連結,則解析路徑中存在的符号連結(如果有的話)。

    如果原來的Path是相對路徑,将其轉換成絕對路徑。

    如果路徑中含有備援資訊,傳回的Path中這些備援資訊會被去除。

連接配接兩個Path

可以使用resolve方法來将兩個Path連接配接起來。該方法的參數是一個字元串。如果該字元串代表的是一個相對路徑,那麼這個路徑會被擴充到原來的路徑 後。如果傳入的字元串是一個絕對路徑,那麼傳回的值就是傳入的這個絕對路徑。例如:

  1.         Path p1 = Paths.get("C:\\home\\joe\\foo");   
  2.         System.out.format("%s%n", p1.resolve("bar")); // 結果是 C:\home\joe\foo\bar
  3.         Paths.get("foo").resolve("c:\\home\joe");       // 結果是  C:\home\joe

複制代碼

建立兩個路徑之間的路徑

這個功能說起來有些繞口,實際的功能就是建立兩個指定的目錄或檔案之間的相對路徑。例如:

  1.         Path p1 = Paths.get("joe/foo");
  2.         Path p2 = Paths.get("sally");

複制代碼

在這個例子中,由于兩個路徑都是相對路徑,沒有其他的 資訊,我們會認為這兩個joe和sally是同一級的兄弟目錄,是以有以下的結果

  1.         Path p1_to_p2 = p1.relativize(p2);   // 結果是 ../../sally
  2.         Path p2_to_p1 = p2.relativize(p1);   // 結果是 ../joe/foo

複制代碼

讓我們看看另外一個例子:

  1.         Path p1 = Paths.get("home");
  2.         Path p3 = Paths.get("home/sally/bar");
  3.         Path p1_to_p3 = p1.relativize(p3);  // 結果是 sally/bar
  4.         Path p3_to_p1 = p3.relativize(p1);  // 結果是 ../..

複制代碼

在這個例子中,兩個路徑共享同一個節點-home, 是以結果并不是../home/sally/bar和../../../home.

如果兩個路徑中有一個是絕對路徑,另外一個是相對路徑,relative方法會抛出異常。如果兩個路徑都是絕對路徑,那麼relative方法的行為和系 統相關,不同的系統可能不同。

我在Windows作業系統下實驗了一下,如果兩個路徑屬于同一個硬碟,那麼可以執行成功,否則會抛出異常。

  1. Path path1 = Paths.get("c:\\abcd\\efg");
  2. Path path2 = Paths.get("c:\\temp");
  3. System.out.println(path1.relativize(path2));        //結果是..\..\temp
  4. System.out.println(path2.relativize(path1));        //結果是..\abcd\efg
  5. Path path3 = Paths.get("c:\\abcd\\efg");
  6. Path path4 = Paths.get("d:\\temp");
  7. System.out.println(path3.relativize(path4));        //抛出異常

複制代碼

Path 的比較

Path提供equals 方法來檢查兩個Path是否相等。但是這裡 要注意的是比較的并不是兩個Path是否指向同一個目錄或者檔案。請看下面的例子:

  1. Path path1 = Paths.get("abcd\\123");
  2. Path path2 = Paths.get("abcd\\123");
  3. Path path3 = Paths.get("abcd\\.\\123");
  4. System.out.println(path1.equals(path2));        //true
  5. System.out.println(path1.equals(path3));        //false
  6. System.out.println(path1.equals(path3.normalize())); //true
  7. System.out.println(path1.equals(path1.toAbsolutePath()));        //false

複制代碼

Path 類還提供了startsWith和endsWith方法,這兩個方法用來檢查路徑是否以指定的字元串開始或者結束,例如:

  1.         Path path = ...;
  2.         Path otherPath = ...;
  3.         Path beginning = Paths.get("/home");
  4.         Path ending = Paths.get("foo");
  5.         if (path.equals(otherPath)) {
  6.             //equality logic here
  7.         } else if (path.startsWith(beginning)) {
  8.             //path begins with "/home"
  9.         } else if (path.endsWith(ending)) {
  10.             //path ends with "foo"
  11.         }

複 制代碼

Path類實作了Iterable接口,iterator方法會傳回一個Iterator對象,該對象中的第一個元素就是原 路徑中最上層(最靠近根節點)的目錄。下面是使用這個方法的例子:

  1.         Path path = ...;
  2.         for (Path name: path) {
  3.             System.out.println(name);
  4.         }

複 制代碼

Path類還實作了Comparable接口,是以可以使用compareTo來比較兩個Path。比較的算法和結果是和文 件系統的提供者和系統平台相關的。大家在使用之前,最後先實驗一下。

Path類還提供了一個方法isSameFile來檢查兩個Path是否指向同一個目錄或檔案。如果作為參數的Path為null,那麼會直接傳回 false,不會去檢查Path指向的檔案是否存在。如果兩Path來自不同的檔案系統提供者,那麼也會直接傳回false,不會去檢查檔案或目錄是否存 在。如果兩個Path執行equals方法的傳回結果為true,那麼這個方法直接傳回true,也不會去檢查檔案或目錄是否存在。其他的情況下是否會去 打開或通路Path指向的檔案或目錄是與具體的實作相關的,也就是說不同的JDK/JRE可能會有不同的行為。

驗證檔案或目錄是否存在

上面所介紹的很多方法都不會去驗證Path指向的檔案或目錄是否存在,隻是操作Path執行個體自身。但是在有些情況下,你 需要通路檔案系統來驗證檔案、目錄存在與否,這時你可以使用exists和notExists方法。需要注意的是!path.exists()并不等于 path.notExists()。當你調用這兩個方法時,有以下3中情況:

  • 檔案或者目錄被證明存在
  • 檔案或者目錄被證明不存在
  • 不知道檔案或目錄是否存在。當程式沒有通路這個文 件或目錄的權限的時候這個情況會發生。

如果exists()和notExists()都傳回false,說明無法驗證該檔案是否存在。

在下面一篇文章中,我們會介紹怎樣進行檔案的操作。