這裡的內容節錄自 Pro Java 7 NIO.2 一書,介紹的是 Path 這個類別,它是 NIO.2 中的一個類別,比起傳統上使用 java.io 中的類別會來的方便,整理如下:
- defining a path & getting info about a path
從以下的程式及輸出,來說明 Path 類別的使用。
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 }
輸出結果如下:
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,免去了一直重複發明輪子的困擾。接下說明如下:
- normailize: 去除"多餘"的部份,什麼是多餘的? 以上面的程式中 path1 和 path 2作比較,path1 有呼叫 normailize,移除了第二行紅色的部份,輸出的結果比較簡節,實際上是指向同一個檔案。
- home directory: 使用者的家目錄,如程式第 16 行所示,因為我是在 Windows 7 中測試,輸出結果為 C:\users\Steven,如果是在 linux 中測試,可能就會是 /home/steven。
- root directory: 程式第 18 行,取得根目錄,以上面的程式看,當然就是 D:\。
- parent directory: 程式第 19 行可以取得上一層的目錄,因此得到 D:\English\ESL\Business。
- 分解完整檔名 (full file name): 程式第 21~27 行是說明如何分解完整的檔名,以往的作法可能會用 String[] folder = filename.split("/"); 這樣的方式處理,現在 Path 類別直接提供了這個功能,21 行是傳回總共有幾層的目錄 (包含檔名),23~25 行則將各層輸出到 console。第 27 行的 subpath 是 Path 類別提供的一個 method,讓程式開發者方便取得完整檔名中所需要的部份。
- 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) 不是一個真實的檔案。這個結果可以讓我們在存取某個目錄下所有檔案時,用來忽略目錄中的捷徑。
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 類別時,可以比較的出來嗎? 看一下程式 …
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 來比較,卻都得不到正確結果! 如下:
not equal -24 The paths locate the same file!
為什麼會是這樣呢? 因為 equals 的比較方式,在 Path 類別中是以 Object.equals 來比較,當然,path1 和 path2 並非相同的兩個物件,就傳回 false。那麼 compareTo 又是怎麼比較的? 它是以字典的順序來比較,只有當兩個檔名的寫法完全相同,才會傳回 0 表示兩個相同,當第一個大於第二個時,傳回正整數,第一個小於第二個時傳回負整數。要注意的是,compareTo 在不同的作業系統中會有不同的反應,例如在 Windows 中,因為 Windows 的檔名是不分大小寫的,所以它在比較時也不分大小寫,在 Linux 中因為檔名有分大小寫,它在比較時就會區分大小寫。程式的第 28 行是正確的比較方法,只有用這個方法才會得到我們真正要的結果!