小師妹學JavaIO之:目錄還是文件
簡介
目錄和文件傻傻分不清楚,目錄和文件的本質到底是什麼?在java中怎麼操縱目錄,怎麼遍歷目錄。本文F師兄會為大家一一講述。
linux中的文件和目錄
小師妹:F師兄,我最近有一個疑惑,java程式碼中好像只有文件沒有目錄呀,是不是當初發明java的大神,一不小心走了神?
F師兄:小師妹真勇氣可嘉呀,敢於質疑權威是從小工到專家的最重要的一步。想想F師兄我,從小沒人提點,老師講什麼我就信什麼,專家說什麼我就聽什麼:股市必上一萬點,房子是給人住的不是給人炒的,原油寶當然是小白理財必備產品….然後,就沒有然後了。
更多精彩內容且看:
- 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
- Spring 5.X系列教程:滿足你對Spring5的一切想像-持續更新
- java程式設計師從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程
更多內容請訪問www.flydean.com
雖然java中沒有目錄的概念只有File文件,而File其實是可以表示目錄的:
public boolean isDirectory()
File中有個isDirectory方法,可以判斷該File是否是目錄。
File和目錄傻傻分不清楚,小師妹,有沒有聯想到點什麼?
小師妹:F師兄,我記得你上次講到Linux下面所有的資源都可以看做是文件,在linux下面文件和目錄的本質是不是一樣的?
對的,在linux下面文件是一等公民,所有的資源都是以文件的形式來區分的。
什麼扇區,邏輯塊,頁之類的底層結構我們就不講了。我們先考慮一下一個文件到底應該包含哪些內容。除了文件本身的數據之外,還有很多元數據的東西,比如文件許可權,所有者,group,創建時間等資訊。
在linux系統中,這兩個部分是分開存儲的。存放數據本身的叫做block,存放元數據的叫做inode。
inode中存儲了block的地址,可以通過inode找到文件實際數據存儲的block地址,從而進行文件訪問。考慮一下大文件可能佔用很多個block,所以一個inode中可以存儲多個block的地址,而一個文件通常來說使用一個inode就夠了。
為了顯示層級關係和方便文件的管理,目錄的數據文件中存放的是該目錄下的文件和文件的inode地址,從而形成了一種一環套一環,圓環套圓環的鏈式關係。
上圖列出了一個通過目錄查找其下文件的環中環布局。
我想java中目錄沒有單獨列出來一個類的原因可能是參考了linux底層的文件布局吧。
目錄的基本操作
因為在java中目錄和文件是公用File這個類的,所以File的基本操作目錄它全都會。
基本上,目錄和文件相比要多注意下面三類方法:
public boolean isDirectory()
public File[] listFiles()
public boolean mkdir()
為什麼說是三類呢?因為還有幾個和他們比較接近的方法,這裡就不一一列舉了。
isDirectory判斷該文件是不是目錄。listFiles列出該目錄下面的所有文件。mkdir創建一個文件目錄。
小師妹:F師兄,之前我們還以目錄的遍歷要耗費比較長的時間,經過你一講解目錄的數據結構,感覺listFiles並不是一個耗時操作呀,所有的數據都已經準備好了,直接讀取出來就行。
對,看問題不要看表面,要看到隱藏在表面的本質內涵。你看師兄我平時不顯山露水,其實是真正的中流砥柱,堪稱公司優秀員工模範。
小師妹:F師兄,那平時也沒看上頭表彰你啥的?哦,我懂了,一定是老闆怕表彰了你引起別人的嫉妒,會讓你的好好大師兄的形象崩塌吧,看來老闆真的懂你呀。
目錄的進階操作
好了小師妹,你懂了就行,下面F師兄給你講一下目錄的進階操作,比如我們怎麼拷貝一個目錄呀?
小師妹,拷貝目錄簡單的F師兄,上次你就教我了:
cp -rf
一個命令的事情不就解決了嗎?難道裡面還隱藏了點秘密?
咳咳咳,秘密倒是沒有,小師妹,我記得你上次說要對java從一而終的,今天師兄給你介紹一個在java中拷貝文件目錄的方法。
其實Files工具類里已經為我們提供了一個拷貝文件的優秀方法:
public static Path copy(Path source, Path target, CopyOption... options)
使用這個方法,我們就可以進行文件的拷貝了。
如果想要拷貝目錄,就遍歷目錄中的文件,循環調用這個copy方法就夠了。
小師妹:且慢,F師兄,如果目錄下面還有目錄的,目錄下還套目錄的情況該怎麼處理?
這就是圈套呀,看我用個遞歸的方法解決它:
public void useCopyFolder() throws IOException {
File sourceFolder = new File("src/main/resources/flydean-source");
File destinationFolder = new File("src/main/resources/flydean-dest");
copyFolder(sourceFolder, destinationFolder);
}
private static void copyFolder(File sourceFolder, File destinationFolder) throws IOException
{
//如果是dir則遞歸遍歷創建dir,如果是文件則直接拷貝
if (sourceFolder.isDirectory())
{
//查看目標dir是否存在
if (!destinationFolder.exists())
{
destinationFolder.mkdir();
log.info("目標dir已經創建: {}",destinationFolder);
}
for (String file : sourceFolder.list())
{
File srcFile = new File(sourceFolder, file);
File destFile = new File(destinationFolder, file);
copyFolder(srcFile, destFile);
}
}
else
{
//使用Files.copy來拷貝具體的文件
Files.copy(sourceFolder.toPath(), destinationFolder.toPath(), StandardCopyOption.REPLACE_EXISTING);
log.info("拷貝目標文件: {}",destinationFolder);
}
}
基本思想就是遇到目錄我就遍歷,遇到文件我就拷貝。
目錄的腰疼操作
小師妹:F師兄,假如我想刪除一個目錄中的文件,或者我們想統計一下這個目錄下面到底有多少個文件該怎麼做呢?
雖然這些操作有點腰疼,還是可以解決的,Files工具類中有個方法叫做walk,返回一個Stream對象,我們可以使用Stream的API來對文件進行處理。
刪除文件:
public void useFileWalkToDelete() throws IOException {
Path dir = Paths.get("src/main/resources/flydean");
Files.walk(dir)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
統計文件:
public void useFileWalkToSumSize() throws IOException {
Path folder = Paths.get("src/test/resources");
long size = Files.walk(folder)
.filter(p -> p.toFile().isFile())
.mapToLong(p -> p.toFile().length())
.sum();
log.info("dir size is: {}",size);
}
總結
本文介紹了目錄的一些非常常見和有用的操作。
本文的例子//github.com/ddean2009/learn-java-io-nio
本文作者:flydean程式那些事
本文鏈接://www.flydean.com/java-io-directory/
本文來源:flydean的部落格
歡迎關注我的公眾號:程式那些事,更多精彩等著您!