Java NIO Path接口和Files類配合操作文件的實(shí)例
Path接口
1、Path表示的是一個(gè)目錄名序列,其后還可以跟著一個(gè)文件名,路徑中第一個(gè)部件是根部件時(shí)就是絕對(duì)路徑,例如 / 或 C:\ ,而允許訪問的根部件取決于文件系統(tǒng);
2、以根部件開始的路徑是絕對(duì)路徑,否則就是相對(duì)路徑;
3、靜態(tài)的Paths.get方法接受一個(gè)或多個(gè)字符串,字符串之間自動(dòng)使用默認(rèn)文件系統(tǒng)的路徑分隔符連接起來(Unix是 /,Windows是 \ ),這就解決了跨平臺(tái)的問題,接著解析連接起來的結(jié)果,如果不是合法路徑就拋出InvalidPathException異常,否則就返回一個(gè)Path對(duì)象;
//假設(shè)是Unix的文件系統(tǒng) Path absolute = Paths.get("/home", "cat"); //絕對(duì)路徑 Path relative = Pahts.get("ixenos", "config", "user.properties"); //相對(duì)路徑
4、由String路徑獲取Path對(duì)象
get還可以獲取一整條路徑(即多個(gè)部件構(gòu)成的單個(gè)字符串),例如從配置文件中讀取路徑:
String baseDir = properties.getProperty("base.dir"); //可能獲得 /opt/ixenos 或者 C:\Program Files\ixenos Path basePath = Paths.get(baseDir);
5、組合或解析路徑
1) 調(diào)用 p.resolve(q) 將按下面的規(guī)則返回一個(gè)Path:如果q是絕對(duì)路徑,則返回q,否則追加路徑返回 p/q 或者 p\q
Path workRelative = Paths.get("work"); Path workPath = basePath.resolve(workRelative); //resolve也可以接受字符串形參 Path workPath = basePath.resolve("work");
2) 調(diào)用 p.resolveSibling("q") 將解析指定路徑 p 的父路徑 o ,并產(chǎn)生兄弟路徑 o/q
Path tempPath = workPath.resolveSibling("temp"); /* 如果workPath是 /opt/ixenos/work 那么將創(chuàng)建 /opt/ixenos/temp */
3) 調(diào)用 p.relativize(r) 將產(chǎn)生一個(gè)冗余路徑q,對(duì)q進(jìn)行解析將產(chǎn)生相對(duì)路徑r,最終r不包含和p的交集路徑
/* pathA為 /home/misty pathB為 /home/ixenos/config 現(xiàn)已pathA對(duì)pathB進(jìn)行相對(duì)化操作,將產(chǎn)生冗余路徑 */ Path pathC = pathA.relativize(pathB); //此時(shí)pathC為 ../ixenos/config /* normalize方法將移除冗余部件 */ Path pathD = pathC.normalize(); //pathD為 /ixenos/config
4) toAbsolutePath 將產(chǎn)生給定路徑的絕對(duì)路徑,從根部件開始
5) Path類還有一些有用的斷開和組合路徑的方法,比如 getParent、getFileName、getRoot//獲得根目錄
6) Path有個(gè)toFile方法用來跟遺留類File類打交道,F(xiàn)ile類也有個(gè)toPath方法
Files工具類
1、讀寫文件
方法簽名:
static path write(Path path, byte[] bytes, OpenOption... options)
static path write(Path path, Iterable<? extends CharSequence> lines, OpenOption... options)
這里只列舉下面用到的方法,更多方法請(qǐng)看API文檔...
其中OpenOption是個(gè)nio接口,StandardOpenOption是其枚舉實(shí)現(xiàn)類,各枚舉實(shí)例功能請(qǐng)查看API文檔
/* Files提供的簡(jiǎn)便方法適用于處理中等長度的文本文件 如果要處理的文件長度較大,或者二進(jìn)制文件,那么還是應(yīng)該使用經(jīng)典的IO流 */ //將文件所有內(nèi)容讀入byte數(shù)組中 byte[] bytes = Files.readAllBytes(path); //傳入Path對(duì)象 //之后可以根據(jù)字符集構(gòu)建字符串 String content = new String(bytes, charset); //也可以直接當(dāng)作行序列讀入 List<String> lines = Files.readAllLines(path, charset); //相反,也可以寫一個(gè)字符串到文件中,默認(rèn)是覆蓋 Files.write(path, content.getBytes(charset)); //傳入byte[] //追加內(nèi)容,根據(jù)參數(shù)決定追加等功能 Files.write(path, content.getBytes(charset), StandardOpenOption.APPEND); //傳入枚舉對(duì)象,打開追加開關(guān) //將一個(gè)行String的集合List寫出到文件中 Files.write(path, lines);
2、復(fù)制、剪切、刪除
方法簽名:
static path copy(Path source, Path target, CopyOption... options)
static path move(Path source, Path target, CopyOption... options)
static void delete(Path path) //如果path不存在文件將拋出異常,此時(shí)調(diào)用下面的比較好
static boolean deleteIfExists(Path path)
這里只列舉下面用到的方法,更多方法請(qǐng)看API文檔...
其中CopyOption是個(gè)nio接口,StandardCopyOption是其枚舉實(shí)現(xiàn)類,各枚舉實(shí)例功能請(qǐng)查看API文檔
其中有個(gè)ATOMIC_MOVE可以填入用來保證原子性操作,要么移動(dòng)成功完成,要么源文件保持在原位置
//復(fù)制 Files.copy(fromPath, toPath); //剪切 Files.move(fromPath, toPath); /* 以上如果toPath已存在,那么操作失敗, 如果要覆蓋,需傳入?yún)?shù)REPLACE_EXISTING 還要復(fù)制文件屬性,傳入COPY_ATTRIBUTES */ Files.copy(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
3、創(chuàng)建文件和目錄
//創(chuàng)建新目錄,除了最后一個(gè)部件,其他必須是已存在的 Files.createDirectory(path); //創(chuàng)建路徑中的中間目錄,能創(chuàng)建不存在的中間部件 Files.createDirectories(path); /* 創(chuàng)建一個(gè)空文件,檢查文件存在,如果已存在則拋出異常 而檢查文件存在是原子性的,因此在此過程中無法執(zhí)行文件創(chuàng)建操作 */ Files.createFile(path); //添加前/后綴創(chuàng)建臨時(shí)文件或臨時(shí)目錄 Path newPath = Files.createTempFile(dir, prefix, suffix); Path newPath = Files.createTempDirectory(dir, prefix);
4、獲取文件信息
略,具體看API文檔,或者corejava page51
5、迭代目錄中的文件
舊的File類有兩個(gè)方法獲取目錄中所有文件構(gòu)成的字符串?dāng)?shù)組,String[] list() 和String[] list(FileFilter filter),但是當(dāng)目錄中包含大量文件時(shí),這兩方法性能會(huì)非常低。
原因分析:
1、//File類list所有文件 public String[] list() { SecurityManager security = System.getSecurityManager(); //文件系統(tǒng)權(quán)限獲取 if (security != null) { security.checkRead(path); } if (isInvalid()) { return null; } return fs.list(this); //底層調(diào)用FileSystem的list } //FileSystem抽象類的list //File類中定義fs是由DefaultFileSystem靜態(tài)生成的 private static final FileSystem fs = DefaultFileSystem.getFileSystem(); //因此我們來看一下DefaultFileSystem類,發(fā)現(xiàn)是生成一個(gè)WinNtFileSystem對(duì)象 class DefaultFileSystem { /** * Return the FileSystem object for Windows platform. */ public static FileSystem getFileSystem() { return new WinNTFileSystem(); } } //而WinNtFileSystem類繼承于FileSystem抽象類,這里我們主要觀察它的list(File file)方法 @Override public native String[] list(File f); /*我們可以看到這是個(gè)native方法,說明list的操作是由操作系統(tǒng)的文件系統(tǒng)控制的,當(dāng)目錄中包含大量的文件時(shí),這個(gè)方法的性能將會(huì)非常低。 由此為了替代,NIO的Files類設(shè)計(jì)了newDirectoryStream(Path dir)及其重載方法,將生成Iterable對(duì)象(可用foreach迭代)*///~ 2、//回調(diào)過濾 public String[] list(FilenameFilter filter) { //采用接口回調(diào) String names[] = list(); //調(diào)用list所有 if ((names == null) || (filter == null)) { return names; } List<String> v = new ArrayList<>(); for (int i = 0 ; i < names.length ; i++) { if (filter.accept(this, names[i])) { //回調(diào)FilenameFileter對(duì)象的accept方法 v.add(names[i]); } } return v.toArray(new String[v.size()]); }
這時(shí)候高科技來了——Files獲得可迭代的目錄流,
傳入一個(gè)目錄Path,遍歷子孫目錄返回一個(gè)目錄Path的Stream,注意這里所有涉及的Path都是目錄而不是文件!
因此,F(xiàn)iles類設(shè)計(jì)了newDirectoryStream(Path dir)及其重載方法,將生成Iterable對(duì)象(可用foreach迭代)
遍歷目錄得到一個(gè)可迭代的子孫文件集合
staticDirectoryStream<Path> |
newDirectoryStream(Path dir)
Opens a directory, returning a
DirectoryStream to iterate over all entries in the directory. |
staticDirectoryStream<Path> |
newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter)
Opens a directory, returning a
DirectoryStream to iterate over the entries in the directory. |
staticDirectoryStream<Path> |
newDirectoryStream(Path dir, String glob) |
返回一個(gè) 目錄流 ,可以看成一個(gè)存放著全部Path的實(shí)現(xiàn)了Iterable的集合,
因此可用迭代器或foreach迭代,只是使用迭代器的時(shí)候要注意不能invoke另一個(gè)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.
示例:
try(DirectoryStream<Path> entries = Files.newDirectoryStream(dir)) { for(Path entry : entries) { ... } }
可以傳入glob參數(shù),即使用glob模式來過濾文件(以取代list(FileFilter filter)):
newDirectoryStream(Path dir, String glob) 注意是String類型
try(DirectoryStream<Path> entries = Files.newDirectoryStream(dir, "*.java")) // { ... }
glob模式
所謂的 glob 模式是指 shell 所使用的簡(jiǎn)化了的正則表達(dá)式。
1.星號(hào) * 匹配路徑組成部分0個(gè)或多個(gè)字符;例如 *.java 匹配當(dāng)前目錄中的所有Java文件
2.兩星號(hào) ** 匹配跨目錄邊界0個(gè)或多個(gè)字符;例如 **.java 匹配在所有子目錄中的Java文件
3.問號(hào)(?)只匹配一個(gè)字符;例如 ????.java 匹配所有四個(gè)字符的Java文件,不包括擴(kuò)展名;使用?是因?yàn)?是通配符不指定數(shù)量
4.[...] 匹配一個(gè)字符集合,可以用連線 [0-9] 和取反符 [!0-9];例如 Test[0-9A-F].java 匹配Testx.java,假設(shè)x是一個(gè)十六進(jìn)制數(shù)字,[0-9A-F]是匹配單個(gè)字符為十六進(jìn)制數(shù)字,比如B(十六進(jìn)制不區(qū)分大小寫)
如果在方括號(hào)中使用短劃線分隔兩個(gè)字符,表示所有在這兩個(gè)字符范圍內(nèi)的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的數(shù)字)。
5.{...} 匹配由逗號(hào)隔開的多個(gè)可選項(xiàng)之中的一個(gè);例如 *.{java,class} 匹配所有Java文件和類class文件
6.\ 轉(zhuǎn)義上述任意模式中的字符;例如 *\** 匹配所有子目錄中文件名包含*的文件,這里為 ** 轉(zhuǎn)義,前面是匹配0個(gè)或多個(gè)字符
下面是網(wǎng)友總結(jié)的Glob模式:
Glob模式 | 描述 |
---|---|
*.txt | 匹配所有擴(kuò)展名為.txt的文件 |
*.{html,htm} | 匹配所有擴(kuò)展名為.html或.htm的文件。{ }用于組模式,它使用逗號(hào)分隔 |
?.txt | 匹配任何單個(gè)字符做文件名且擴(kuò)展名為.txt的文件 |
. | 匹配所有含擴(kuò)展名的文件 |
C:\Users\* | 匹配所有在C盤Users目錄下的文件。反斜線“\”用于對(duì)緊跟的字符進(jìn)行轉(zhuǎn)義 |
/home/** | UNIX平臺(tái)上匹配所有/home目錄及子目錄下的文件。**用于匹配當(dāng)前目錄及其所有子目錄 |
[xyz].txt | 匹配所有單個(gè)字符作為文件名,且單個(gè)字符只含“x”或“y”或“z”三種之一,且擴(kuò)展名為.txt的文件。方括號(hào)[]用于指定一個(gè)集合 |
[a-c].txt | 匹配所有單個(gè)字符作為文件名,且單個(gè)字符只含“a”或“b”或“c”三種之一,且擴(kuò)展名為.txt的文件。減號(hào)“-”用于指定一個(gè)范圍,且只能用在方括號(hào)[]內(nèi) |
[!a].txt | 匹配所有單個(gè)字符作為文件名,且單個(gè)字符不能包含字母“a”,且擴(kuò)展名為.txt的文件。嘆號(hào)“!”用于否定 |
遍歷得到某個(gè)目錄的所有子孫文件集合再迭代不夠爽?來,我們來直接遍歷某個(gè)目錄的所有子孫成員(包括目錄和文件)
我們可以調(diào)用Files類的walkFileTree方法,并傳入一個(gè)FileVisitor接口類型的對(duì)象(還有更多方法在API里等你發(fā)現(xiàn)……)
/*傳入一個(gè)FileVisitor子類的匿名對(duì)象*/ Files.walkFileTree(dir, new SimpleFileVisitor<Path>() { //walkFileTree回調(diào)此方法來遍歷所有子孫 public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { if(attrs.isDirectory()) //自定義的選擇,屬于業(yè)務(wù)代碼,這和walkFileTree的宗旨(遍歷所有子孫成員)無關(guān) System.out.println(path); return FileVisitResult.CONTINUE; } public FileVisitResult visitFileFailed(Path path, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } });
咱們來總結(jié)一下,
Files.newDirectoryStream(Path dir) 遍歷后返回一個(gè)可迭代的子孫文件集合;
Files.walkFileTree(Path dir, FileVisitor fv) 是一個(gè)遍歷子孫目錄和文件的過程;
ZIP文件系統(tǒng)
由上文知道,Paths類會(huì)在默認(rèn)的文件系統(tǒng)中查找路徑,即在用戶本地磁盤中的文件。
其實(shí),我們也可以有其他的文件系統(tǒng),比如ZIP文件系統(tǒng)。
/*假設(shè)zipname是某個(gè)ZIP文件的名字*/ FileSystem fs = FileSystems.newFileSystem(Paths.get(zipname), null);
上述代碼將建立一個(gè)基于zipname的文件系統(tǒng),它包含ZIP文檔中的所有文件。
1)如果知道文件名(String類型),那么從這個(gè)ZIP文檔中復(fù)制出這個(gè)文件就很容易:
Files.copy(fs.getPath(fileName), targetPath);
Q:fs.getPath是使用了ZIP文件系統(tǒng)來getPath,那么默認(rèn)的文件系統(tǒng)能調(diào)用嗎?
A:能。FileSystem類中有一個(gè)靜態(tài)的getDefault()方法,返回一個(gè)默認(rèn)的文件系統(tǒng)對(duì)象,同樣可以由文件名getPath。
具體getPath(String name)是遍歷還是隨機(jī)訪問,有空再去看源碼實(shí)現(xiàn)。
2)要列出ZIP文檔中的所有文件,同樣可以用walkFileTree遍歷文件樹
FileSystem fs = FileSystems.newFileSystem(Paths.get(fileName), null); //walkFileTree需要傳入一個(gè)要被遍歷的目錄Path,和一個(gè)FileVisitor對(duì)象 Files.walkFileTree(fs.getPath("/"), newSimpleFileVisitor<Path>(){ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws Exception{ System.out.println(file); return FileVisitResult.CONTINUE; });
以上這篇Java NIO Path接口和Files類配合操作文件的實(shí)例就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
使用FeignClient設(shè)置動(dòng)態(tài)Url
這篇文章主要介紹了使用FeignClient設(shè)置動(dòng)態(tài)Url方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06使用JPA雙向多對(duì)多關(guān)聯(lián)關(guān)系@ManyToMany
這篇文章主要介紹了使用JPA雙向多對(duì)多關(guān)聯(lián)關(guān)系@ManyToMany,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06springboot自動(dòng)掃描添加的BeanDefinition源碼實(shí)例詳解
這篇文章主要給大家介紹了關(guān)于springboot自動(dòng)掃描添加的BeanDefinition的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-02-02idea與eclipse項(xiàng)目相互導(dǎo)入的過程(圖文教程)
這篇文章主要介紹了idea與eclipse項(xiàng)目相互導(dǎo)入的過程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03SpringBoot打印系統(tǒng)執(zhí)行的sql語句及日志配置指南
這篇文章主要給大家介紹了關(guān)于SpringBoot打印系統(tǒng)執(zhí)行的sql語句及日志配置的相關(guān)資料,在Java SpringBoot項(xiàng)目中如果使用了Mybatis框架,默認(rèn)情況下執(zhí)行的所有SQL操作都不會(huì)打印日志,需要的朋友可以參考下2023-10-10SpringBoot+MyBatisPlus對(duì)Map中Date格式轉(zhuǎn)換處理的方法詳解
在?SpringBoot?項(xiàng)目中,?如何統(tǒng)一?JSON?格式化中的日期格式。本文將為大家介紹一種方法:利用MyBatisPlus實(shí)現(xiàn)對(duì)Map中Date格式轉(zhuǎn)換處理,需要的可以參考一下2022-10-10