@author ixenoshtml
一、Path表示的是一個目錄名序列,其後還能夠跟着一個文件名,路徑中第一個部件是根部件時就是絕對路徑,例如 / 或 C:\ ,而容許訪問的根部件取決於文件系統;java
二、以根部件開始的路徑是絕對路徑,不然就是相對路徑;正則表達式
三、靜態的Paths.get方法接受一個或多個字符串,字符串之間自動使用默認文件系統的路徑分隔符鏈接起來(Unix是 /,Windows是 \ ),這就解決了跨平臺的問題,接着解析鏈接起來的結果,若是不是合法路徑就拋出InvalidPathException異常,不然就返回一個Path對象;shell
1 //假設是Unix的文件系統 2 Path absolute = Paths.get("/home", "cat"); //絕對路徑 3 4 Path relative = Pahts.get("ixenos", "config", "user.properties"); //相對路徑
四、由String路徑獲取Path對象數組
get還能夠獲取一整條路徑(即多個部件構成的單個字符串),例如從配置文件中讀取路徑:ide
1 String baseDir = properties.getProperty("base.dir"); 2 //可能得到 /opt/ixenos 或者 C:\Program Files\ixenos 3 Path basePath = Paths.get(baseDir);
五、組合或解析路徑工具
1) 調用 p.resolve(q) 將按下面的規則返回一個Path:若是q是絕對路徑,則返回q,不然追加路徑返回 p/q 或者 p\q性能
1 Path workRelative = Paths.get("work"); 2 Path workPath = basePath.resolve(workRelative); 3 4 //resolve也能夠接受字符串形參 5 Path workPath = basePath.resolve("work");
2) 調用 p.resolveSibling("q") 將解析指定路徑 p 的父路徑 o ,並產生兄弟路徑 o/qthis
1 Path tempPath = workPath.resolveSibling("temp"); 2 /* 3 若是workPath是 /opt/ixenos/work 4 那麼將建立 /opt/ixenos/temp 5 */
3) 調用 p.relativize(r) 將產生一個冗餘路徑q,對q進行解析將產生相對路徑r,最終r不包含和p的交集路徑spa
1 /* 2 pathA爲 /home/misty 3 pathB爲 /home/ixenos/config 4 5 現已pathA對pathB進行相對化操做,將產生冗餘路徑 6 */ 7 Path pathC = pathA.relativize(pathB); //此時pathC爲 ../ixenos/config 8 9 /* 10 normalize方法將移除冗餘部件 11 */ 12 Path pathD = pathC.normalize(); //pathD爲 /ixenos/config
4) toAbsolutePath 將產生給定路徑的絕對路徑,從根部件開始
5) Path類還有一些有用的斷開和組合路徑的方法,好比 getParent、getFileName、getRoot//得到根目錄
6) Path有個toFile方法用來跟遺留類File類打交道,File類也有個toPath方法
方法簽名:
static path write(Path path, byte[] bytes, OpenOption... options)
static path write(Path path, Iterable<? extends CharSequence> lines, OpenOption... options)
其中OpenOption是個nio接口,StandardOpenOption是其枚舉實現類,各枚舉實例功能請查看API文檔
1 /* 2 Files提供的簡便方法適用於處理中等長度的文本文件 3 4 若是要處理的文件長度較大,或者二進制文件,那麼仍是應該使用經典的IO流 5 6 */ 7 8 //將文件全部內容讀入byte數組中 9 byte[] bytes = Files.readAllBytes(path); //傳入Path對象 10 11 //以後能夠根據字符集構建字符串 12 String content = new String(bytes, charset); 13 14 //也能夠直接看成行序列讀入 15 List<String> lines = Files.readAllLines(path, charset); 16 17 //相反,也能夠寫一個字符串到文件中,默認是覆蓋 18 Files.write(path, content.getBytes(charset)); //傳入byte[] 19 20 //追加內容,根據參數決定追加等功能 21 Files.write(path, content.getBytes(charset), StandardOpenOption.APPEND); //傳入枚舉對象,打開追加開關 22 23 //將一個行String的集合List寫出到文件中 24 Files.write(path, lines);
方法簽名:
static path copy(Path source, Path target, CopyOption... options)
static path move(Path source, Path target, CopyOption... options)
static void delete(Path path) //若是path不存在文件將拋出異常,此時調用下面的比較好
static boolean deleteIfExists(Path path)
其中CopyOption是個nio接口,StandardCopyOption是其枚舉實現類,各枚舉實例功能請查看API文檔
其中有個ATOMIC_MOVE能夠填入用來保證原子性操做,要麼移動成功完成,要麼源文件保持在原位置
1 //複製 2 Files.copy(fromPath, toPath); 3 4 //剪切 5 Files.move(fromPath, toPath); 6 7 /* 8 以上若是toPath已存在,那麼操做失敗, 9 若是要覆蓋,需傳入參數REPLACE_EXISTING 10 還要複製文件屬性,傳入COPY_ATTRIBUTES 11 */ 12 Files.copy(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
1 //建立新目錄,除了最後一個部件,其餘必須是已存在的 2 Files.createDirectory(path); 3 4 //建立路徑中的中間目錄,能建立不存在的中間部件 5 Files.createDirectories(path); 6 7 /* 8 建立一個空文件,檢查文件存在,若是已存在則拋出異常 9 而檢查文件存在是原子性的,所以在此過程當中沒法執行文件建立操做 10 */ 11 Files.createFile(path); 12 13 //添加前/後綴建立臨時文件或臨時目錄 14 Path newPath = Files.createTempFile(dir, prefix, suffix); 15 Path newPath = Files.createTempDirectory(dir, prefix);
略,具體看API文檔,或者corejava page51
舊的File類有兩個方法獲取目錄中全部文件構成的字符串數組,String[] list() 和String[] list(FileFilter filter),可是當目錄中包含大量文件時,這兩方法性能會很是低。
緣由分析:
1 一、//File類list全部文件 2 public String[] list() { 3 SecurityManager security = System.getSecurityManager(); //文件系統權限獲取 4 if (security != null) { 5 security.checkRead(path); 6 } 7 if (isInvalid()) { 8 return null; 9 } 10 return fs.list(this); //底層調用FileSystem的list 11 } 12 13 //FileSystem抽象類的list 14 //File類中定義fs是由DefaultFileSystem靜態生成的 15 private static final FileSystem fs = DefaultFileSystem.getFileSystem(); 16 17 //所以咱們來看一下DefaultFileSystem類,發現是生成一個WinNtFileSystem對象 18 class DefaultFileSystem { 19 20 /** 21 * Return the FileSystem object for Windows platform. 22 */ 23 public static FileSystem getFileSystem() { 24 return new WinNTFileSystem(); 25 } 26 } 27 28 29 //而WinNtFileSystem類繼承於FileSystem抽象類,這裏咱們主要觀察它的list(File file)方法 30 @Override 31 public native String[] list(File f); 32 /*咱們能夠看到這是個native方法,說明list的操做是由操做系統的文件系統控制的,當目錄中包含大量的文件時,這個方法的性能將會很是低。 33 由此爲了替代,NIO的Files類設計了newDirectoryStream(Path dir)及其重載方法,將生成Iterable對象(可用foreach迭代)*///~ 34 38 39 二、//回調過濾 40 public String[] list(FilenameFilter filter) { //採用接口回調 41 String names[] = list(); //調用list全部 42 if ((names == null) || (filter == null)) { 43 return names; 44 } 45 List<String> v = new ArrayList<>(); 46 for (int i = 0 ; i < names.length ; i++) { 47 if (filter.accept(this, names[i])) { //回調FilenameFileter對象的accept方法 48 v.add(names[i]); 49 } 50 } 51 return v.toArray(new String[v.size()]); 52 }
所以,Files類設計了newDirectoryStream(Path dir)及其重載方法,將生成Iterable對象(可用foreach迭代)
遍歷目錄獲得一個可迭代的子孫文件集合
static DirectoryStream<Path> |
newDirectoryStream(Path dir)
Opens a directory, returning a
DirectoryStream to iterate over all entries in the directory.
|
static DirectoryStream<Path> |
newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter)
Opens a directory, returning a
DirectoryStream to iterate over the entries in the directory.
|
static DirectoryStream<Path> |
newDirectoryStream(Path dir, String glob) |
返回一個 目錄流 ,能夠當作一個存放着所有Path的實現了Iterable的集合,
所以可用迭代器或foreach迭代,只是使用迭代器的時候要注意不能invoke另外一個Iterator:
While DirectoryStream
extends Iterable
, it is not a general-purpose Iterable
as it supports only a single Iterator
; invoking the iterator
method to obtain a second or subsequent iterator throws IllegalStateException
.
示例:
1 try(DirectoryStream<Path> entries = Files.newDirectoryStream(dir)) 2 { 3 for(Path entry : entries) 4 { 5 ... 6 } 7 }
能夠傳入glob參數,即便用glob模式來過濾文件(以取代list(FileFilter filter)):
newDirectoryStream(Path dir, String glob) 注意是String類型
1 try(DirectoryStream<Path> entries = Files.newDirectoryStream(dir, "*.java")) // 2 { 3 ... 4 }
glob模式
所謂的 glob 模式是指 shell 所使用的簡化了的正則表達式。
1.星號 * 匹配路徑組成部分0個或多個字符;例如 *.java 匹配當前目錄中的全部Java文件
2.兩星號 ** 匹配跨目錄邊界0個或多個字符;例如 **.java 匹配在全部子目錄中的Java文件
3.問號(?)只匹配一個字符;例如 ????.java 匹配全部四個字符的Java文件,不包括擴展名;使用?是由於*是通配符不指定數量
4.[...] 匹配一個字符集合,能夠用連線 [0-9] 和取反符 [!0-9];例如 Test[0-9A-F].java 匹配Testx.java,假設x是一個十六進制數字,[0-9A-F]是匹配單個字符爲十六進制數字,好比B(十六進制不區分大小寫)
若是在方括號中使用短劃線分隔兩個字符,表示全部在這兩個字符範圍內的均可以匹配(好比 [0-9] 表示匹配全部 0 到 9 的數字)。
5.{...} 匹配由逗號隔開的多個可選項之中的一個;例如 *.{java,class} 匹配全部Java文件和類class文件
6.\ 轉義上述任意模式中的字符;例如 *\** 匹配全部子目錄中文件名包含*的文件,這裏爲 ** 轉義,前面是匹配0個或多個字符
下面是網友總結的Glob模式:
Glob模式 | 描述 |
---|---|
*.txt | 匹配全部擴展名爲.txt的文件 |
*.{html,htm} | 匹配全部擴展名爲.html或.htm的文件。{ }用於組模式,它使用逗號分隔 |
?.txt | 匹配任何單個字符作文件名且擴展名爲.txt的文件 |
. | 匹配全部含擴展名的文件 |
C:\Users\* | 匹配全部在C盤Users目錄下的文件。反斜線「\」用於對緊跟的字符進行轉義 |
/home/** | UNIX平臺上匹配全部/home目錄及子目錄下的文件。**用於匹配當前目錄及其全部子目錄 |
[xyz].txt | 匹配全部單個字符做爲文件名,且單個字符只含「x」或「y」或「z」三種之一,且擴展名爲.txt的文件。方括號[]用於指定一個集合 |
[a-c].txt | 匹配全部單個字符做爲文件名,且單個字符只含「a」或「b」或「c」三種之一,且擴展名爲.txt的文件。減號「-」用於指定一個範圍,且只能用在方括號[]內 |
[!a].txt | 匹配全部單個字符做爲文件名,且單個字符不能包含字母「a」,且擴展名爲.txt的文件。歎號「!」用於否認 |
咱們能夠調用Files類的walkFileTree方法,並傳入一個FileVisitor接口類型的對象(還有更多方法在API裏等你發現……)
1 /*傳入一個FileVisitor子類的匿名對象*/ 2 Files.walkFileTree(dir, new SimpleFileVisitor<Path>() 3 { 4 //walkFileTree回調此方法來遍歷全部子孫 5 public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException 6 { 7 if(attrs.isDirectory()) //自定義的選擇,屬於業務代碼,這和walkFileTree的宗旨(遍歷全部子孫成員)無關 8 System.out.println(path); 9 return FileVisitResult.CONTINUE; 10 } 11 12 public FileVisitResult visitFileFailed(Path path, IOException exc) throws IOException 13 { 14 return FileVisitResult.CONTINUE; 15 } 16 });
Files.newDirectoryStream(Path dir) 遍歷後返回一個可迭代的子孫文件集合;
Files.walkFileTree(Path dir, FileVisitor fv) 是一個遍歷子孫目錄和文件的過程;
由上文知道,Paths類會在默認的文件系統中查找路徑,即在用戶本地磁盤中的文件。
其實,咱們也能夠有其餘的文件系統,好比ZIP文件系統。
1 /*假設zipname是某個ZIP文件的名字*/ 2 FileSystem fs = FileSystems.newFileSystem(Paths.get(zipname), null);
上述代碼將創建一個基於zipname的文件系統,它包含ZIP文檔中的全部文件。
1)若是知道文件名(String類型),那麼從這個ZIP文檔中複製出這個文件就很容易:
1 Files.copy(fs.getPath(fileName), targetPath);
Q:fs.getPath是使用了ZIP文件系統來getPath,那麼默認的文件系統能調用嗎?
A:能。FileSystem類中有一個靜態的getDefault()方法,返回一個默認的文件系統對象,一樣能夠由文件名getPath。
具體getPath(String name)是遍歷仍是隨機訪問,有空再去看源碼實現。
2)要列出ZIP文檔中的全部文件,一樣能夠用walkFileTree遍歷文件樹
1 FileSystem fs = FileSystems.newFileSystem(Paths.get(fileName), null); 2 3 //walkFileTree須要傳入一個要被遍歷的目錄Path,和一個FileVisitor對象 4 Files.walkFileTree(fs.getPath("/"), 5 newSimpleFileVisitor<Path>(){ 6 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws Exception{ 7 System.out.println(file); 8 return FileVisitResult.CONTINUE; 9 });