Working with the Path Class

這裡的內容節錄自 Pro Java 7 NIO.2 一書,介紹的是 Path 這個類別,它是 NIO.2 中的一個類別,比起傳統上使用 java.io 中的類別會來的方便,整理以下:html

  • defining a path & getting info about a path

從如下的程式及輸出,來說明 Path 類別的使用。java

 1 package idv.steven.nio2.path;
 2 
 3 import java.nio.file.Path;
 4 import java.nio.file.Paths;
 5 
 6 public class Normalize {
 7 
 8     public static void main(String[] args) {
 9         String filename = "D:/English/./ESL/Business/ESL Podcast 165 - A Practical Joke.mp3";
10         Path path1 = Paths.get(filename).normalize();
11         System.out.println(path1.toString());
12         
13         Path path2 = Paths.get(filename);
14         System.out.println(path2.toString());
15         
16         System.out.println(System.getProperty("user.home"));
17         
18         System.out.println("Root of this path: " + path1.getRoot());
19         System.out.println("Parent: " + path1.getParent());
20         
21         System.out.println("Number of name elements in path: " + path1.getNameCount());
22         
23         for (int i = 0; i < path1.getNameCount(); i++) {
24             System.out.println("Name element " + i + " is: " + path1.getName(i));
25         }
26         
27         System.out.println("Subpath (0,3): " + path1.subpath(0, 3));
28         
29         System.out.println("Filename: " + path1.getFileName().toString());
30     }
31 }

輸出結果以下:linux

D:\English\ESL\Business\ESL Podcast 165 - A Practical Joke.mp3
D:\English\.\ESL\Business\ESL Podcast 165 - A Practical Joke.mp3
C:\Users\Steven
Root of this path: D:\
Parent: D:\English\ESL\Business
Number of name elements in path: 4
Name element 0 is: English
Name element 1 is: ESL
Name element 2 is: Business
Name element 3 is: ESL Podcast 165 - A Practical Joke.mp3
Subpath (0,3): English\ESL\Business
Filename: ESL Podcast 165 - A Practical Joke.mp3

在 java.io 中提供的類別,對於檔案系統中的檔名處理,都是交由程式開發者透過處理字串的方式來進行,於是能够看到程式中必然會出現許多處理路徑、檔名的 method 或類別,Path 直接提供了這些程式開發者须要的 method,免去了一直重複發明輪子的困擾。接下說明以下:this

    1. normailize: 去除"多餘"的部份,什麼是多餘的? 以上面的程式中 path1 和 path 2做比較,path1 有呼叫 normailize,移除了第二行紅色的部份,輸出的結果比較簡節,實際上是指向同一個檔案。
    2. home directory: 使用者的家目錄,如程式第 16 行所示,因為我是在 Windows 7 中測試,輸出結果為 C:\users\Steven,若是是在 linux 中測試,可能就會是 /home/steven。
    3. root directory: 程式第 18 行,取得根目錄,以上面的程式看,當然就是 D:\。
    4. parent directory: 程式第 19 行能够取得上一層的目錄,所以获得 D:\English\ESL\Business。
    5. 分解完整檔名 (full file name): 程式第 21~27 行是說明如何分解完整的檔名,以往的做法可能會用 String[] folder = filename.split("/"); 這樣的方式處理,現在 Path 類別直接提供了這個功能,21 行是傳回總共有幾層的目錄 (包含檔名),23~25 行則將各層輸出到 console。第 27 行的 subpath 是 Path 類別提供的一個 method,讓程式開發者方便取得完整檔名中所须要的部份。
    6. file name: 第 29 行是取得檔名的方法。
  •  ignore symbol link
 1 package idv.steven.nio2.path;
 2 
 3 import java.io.IOException;
 4 import java.nio.file.LinkOption;
 5 import java.nio.file.Path;
 6 import java.nio.file.Paths;
 7 
 8 public class Convert {
 9 
10     public static void main(String[] args) throws IOException {
11         //String filename = "C:/Java/jdk1.7.0_67/README.html";
12         String filename = "D:/README.html";
13         Path path = Paths.get(filename).normalize();
14         System.out.println("path: " + path);
15         System.out.println("path: " + path.toAbsolutePath());
16         System.out.println("real path: " + path.toRealPath(LinkOption.NOFOLLOW_LINKS));
17     }
18 }

在 C:/Java/jdk1.7.0_67 目錄下有個 README.html 的檔案,我們在 D:/ 下创建一個捷徑指向那個檔案,然後以下面的程式分別用 toAbsolutePath 及 toRealPath 存取,會發現當用 toRealPath 存取時,會拋出 exception,以下,顯示該捷徑 (在 linux 下則為 symbol link) 不是一個真實的檔案。這個結果能够讓我們在存取某個目錄下全部檔案時,用來忽略目錄中的捷徑。spa

path: D:\README.html
path: D:\README.html
Exception in thread "main" java.nio.file.NoSuchFileException: D:\README.html
	at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79)
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:90)
	at sun.nio.fs.WindowsLinkSupport.getRealPath(WindowsLinkSupport.java:259)
	at sun.nio.fs.WindowsPath.toRealPath(WindowsPath.java:836)
	at sun.nio.fs.WindowsPath.toRealPath(WindowsPath.java:44)
	at idv.steven.nio2.path.Convert.main(Convert.java:16)
  • comparing two paths

有時候,兩個檔案的完整檔名寫法或有不一样,但其實指的是同一個檔案,當使用 Path 類別時,能够比較的出來嗎? 看一下程式 …code

 1 package idv.steven.nio2.path;
 2 
 3 import java.io.IOException;
 4 import java.nio.file.Files;
 5 import java.nio.file.Path;
 6 import java.nio.file.Paths;
 7 
 8 public class Compare {
 9 
10     public static void main(String[] args) {
11         String filename1 = "D:/English/./ESL/Business/ESL Podcast 165 - A Practical Joke.mp3";
12         String filename2 = "/English/./ESL/Business/ESL Podcast 165 - A Practical Joke.mp3";
13         
14         Path path1 = Paths.get(filename1);
15         Path path2 = Paths.get(filename2);
16         
17         if (path1.equals(path2)) {
18             System.out.println("equal");
19         }
20         else {
21             System.out.println("not equal");
22         }
23         
24         int compare = path1.compareTo(path2);
25         System.out.println(compare);
26         
27         try {
28             boolean check = Files.isSameFile(path1, path2);
29             if(check){
30                 System.out.println("The paths locate the same file!"); //true
31             } else {
32                 System.out.println("The paths does not locate the same file!");
33             }
34         } 
35         catch (IOException e) {
36             System.out.println(e.getMessage());
37         }
38     }
39 }

上面程式中 filename1 和 filename2 是同一個檔案,我們先用 equals 及 compareTo 來比較,卻都得不到正確結果! 以下:orm

not equal
-24
The paths locate the same file!

為什麼會是這樣呢? 因為 equals 的比較方式,在 Path 類別中是以  Object.equals 來比較,當然,path1 和 path2 並非相同的兩個物件,就傳回 false。那麼 compareTo 又是怎麼比較的? 它是以字典的順序來比較,只有當兩個檔名的寫法彻底相同,才會傳回 0 表示兩個相同,當第一個大於第二個時,傳回正整數,第一個小於第二個時傳回負整數。要注意的是,compareTo 在不一样的做業系統中會有不一样的反應,例如在 Windows 中,因為 Windows 的檔名是不分大小寫的,因此它在比較時也不分大小寫,在 Linux 中因為檔名有分大小寫,它在比較時就會區分大小寫。程式的第 28 行是正確的比較方法,只有用這個方法才會获得我們真正要的結果!htm