java7 uri,细数Java8中那些让人纵享丝滑的文件操作
在丑陋的 Java I/O 編程方式誕生多年以后,Java終于簡化了文件讀寫的基本操作。
打開并讀取文件對于大多數編程語言來是非常常用的,由于 I/O 糟糕的設計以至于很少有人能夠在不依賴其他參考代碼的情況下完成打開文件的操作。
在 Java7 中對此引入了巨大的改進。這些新元素被放在java.nio.file包下面,過去人們通常把nio中的n理解為new即新的io,現在更應該當成是non-blocking非阻塞io(io就是input/output輸入/輸出)。java.nio.file庫終于將 Java 文件操作帶到與其他編程語言相同的水平。最重要的是 Java8 新增的 streams 與文件結合使得文件操作編程變得更加優雅。
看一下文件操作的兩個基本組件:
文件或者目錄的路徑;
文件本身。
文件和目錄路徑
一個Path對象表示一個文件或者目錄的路徑,是一個跨操作系統(OS)和文件系統的抽象,目的是在構造路徑時不必關注底層操作系統,代碼可以在不進行修改的情況下運行在不同的操作系統上。java.nio.file.Paths類包含一個重載方法static get(),該方法接受一系列String字符串或一個統一資源標識符(URI)作為參數,并且進行轉換返回一個Path對象。
當toString()方法生成完整形式的路徑,getFileName()方法總是返回當前文件名。
通過使用Files工具類,可以測試一個文件是否存在,測試是否是一個”普通”文件還是一個目錄等等。”Nofile.txt”這個示例展示我們描述的文件可能并不在指定的位置;這樣可以允許你創建一個新的路徑。”PathInfo.java”存在于當前目錄中,最初它只是沒有路徑的文件名,但它仍然被檢測為”存在”。一旦我們將其轉換為絕對路徑,我們將會得到一個從”C:”盤(因為我們是在Windows機器下進行測試)開始的完整路徑,現在它也擁有一個父路徑。
“真實”路徑的定義在文檔中有點模糊,因為它取決于具體的文件系統。例如,如果文件名不區分大小寫,即使路徑由于大小寫的緣故而不是完全相同,也可能得到肯定的匹配結果。在這樣的平臺上,toRealPath()將返回實際情況下的Path,并且還會刪除任何冗余元素。
這里你會看到URI看起來只能用于描述文件,實際上URI可以用于描述更多的東西;通過 維基百科 可以了解更多細節。現在我們成功地將URI轉為一個Path對象。
Path中看到一些有點欺騙的東西,這就是調用toFile()方法會生成一個File對象。聽起來似乎可以得到一個類似文件的東西(畢竟被稱為File),但是這個方法的存在僅僅是為了向后兼容。雖然看上去應該被稱為”路徑”,實際上卻應該表示目錄或者文件本身。這是個非常草率并且令人困惑的命名,但是由于java.nio.file的存在我們可以安全地忽略它的存在。
選取路徑部分片段
Path對象可以非常容易地生成路徑的某一部分:
可以通過getName()來索引Path的各個部分,直到達到上限getNameCount()。Path也實現了Iterable接口,因此我們也可以通過增強的 for-each 進行遍歷。請注意,即使路徑以.java結尾,使用endsWith()方法也會返回false。這是因為使用endsWith()比較的是整個路徑部分,而不會包含文件路徑的后綴。通過使用startsWith()和endsWith()也可以完成路徑的遍歷。但是我們可以看到,遍歷Path對象并不包含根路徑,只有使用startsWith()檢測根路徑時才會返回true。
路徑分析
Files工具類包含一系列完整的方法用于獲得Path相關的信息。
在調用最后一個測試方法getPosixFilePermissions()之前我們需要確認一下當前文件系統是否支持Posix接口,否則會拋出運行時異常。
Paths的增減修改
我們必須能通過對Path對象增加或者刪除一部分來構造一個新的Path對象。我們使用relativize()移除Path的根路徑,使用resolve()添加Path的尾路徑(不一定是“可發現”的名稱)。
對于下面代碼中的示例,我使用relativize()方法從所有的輸出中移除根路徑,部分原因是為了示范,部分原因是為了簡化輸出結果,這說明你可以使用該方法將絕對路徑轉為相對路徑。
這個版本的代碼中包含id,以便于跟蹤輸出結果:
目錄
Files工具類包含大部分我們需要的目錄操作和文件操作方法。出于某種原因,它們沒有包含刪除目錄樹相關的方法
刪除目錄樹的方法實現依賴于Files.walkFileTree(),”walking” 目錄樹意味著遍歷每個子目錄和文件。Visitor 設計模式提供了一種標準機制來訪問集合中的每個對象,然后你需要提供在每個對象上執行的操作。
此操作的定義取決于實現的FileVisitor的四個抽象方法,包括:
preVisitDirectory()
在訪問目錄中條目之前在目錄上運行。
visitFile():調用目錄中的文件
visitFileFailed()
調用無法被訪問的文件。如果該文件的屬性不能被讀取,該文件是無法打開一個目錄,以及其他原因,該方法被調用。
postVisitDirectory()
在訪問目錄中條目之后在目錄上運行,包括所有的子目錄。
為了簡化,java.nio.file.SimpleFileVisitor提供了所有方法的默認實現
在自己的匿名內部類中,只需要重寫非標準行為的方法:visitFile()和postVisitDirectory()實現刪除文件和刪除目錄。兩者都應該返回標志位決定是否繼續訪問
作為探索目錄操作的一部分,現在我們可以有條件地刪除已存在的目錄。在以下例子中,makeVariant()接受基本目錄測試,并通過旋轉部件列表生成不同的子目錄路徑。這些旋轉與路徑分隔符sep使用String.join()貼在一起,然后返回一個Path對象。
如果你對于已經存在的目錄調用createDirectory()將會拋出異常。createFile()使用參數Path創建一個空文件;resolve()將文件名添加到test Path的末尾。
我們嘗試使用createDirectory()來創建多級路徑,但是這樣會拋出異常,因為這個方法只能創建單級路徑。我已經將populateTestDir()作為一個單獨的方法,因為它將在后面的例子中被重用。對于每一個變量variant,我們都能使用createDirectories()創建完整的目錄路徑,然后使用此文件的副本以不同的目標名稱填充該終端目錄。然后我們使用createTempFile()生成一個臨時文件。
在調用populateTestDir()之后,我們在test目錄下面下面創建一個臨時目錄。請注意,createTempDirectory()只有名稱的前綴選項。與createTempFile()不同,我們再次使用它將臨時文件放入新的臨時目錄中。你可以從輸出中看到,如果未指定后綴,它將默認使用”.tmp”作為后綴。
為了展示結果,我們首次使用看起來很有希望的newDirectoryStream(),但事實證明這個方法只是返回test目錄內容的 Stream 流,并沒有更多的內容。要獲取目錄樹的全部內容的流,請使用Files.walk()。
文件系統
為了完整起見,我們需要一種方法查找文件系統相關的其他信息。在這里,我們使用靜態的FileSystems工具類獲取”默認”的文件系統,但也可以在Path對象上調用getFileSystem()以獲取創建該Path的文件系統。
可以獲得給定 URI 的文件系統,還可以構建新的文件系統(對于支持它的操作系統)。
路徑監聽
通過WatchService可以設置一個進程對目錄中的更改做出響應。
一旦我們從FileSystem中得到了WatchService對象,我們將其注冊到test路徑以及我們感興趣的項目的變量參數列表中,可以選擇
ENTRY_CREATE
ENTRY_DELETE
ENTRY_MODIFY(其中創建和刪除不屬于修改)。
接下來對watcher.take()的調用會在發生某些事情之前停止所有操作,所以我們希望deltxtfiles()能夠并行運行以便生成我們感興趣的事件。為了實現這個目的,通過調用Executors.newSingleThreadScheduledExecutor()產生一個ScheduledExecutorService對象,然后調用schedule()方法傳遞所需函數的方法引用,并且設置在運行之前應該等待的時間。
此時,watcher.take()將等待并阻塞在這里。當目標事件發生時,會返回一個包含WatchEvent的Watchkey對象。
如果說”監視這個目錄”,自然會包含整個目錄和下面子目錄,但實際上的:只會監視給定的目錄,而不是下面的所有內容。如果需要監視整個樹目錄,必須在整個樹的每個子目錄上放置一個Watchservice。
文件查找
粗糙的方法,在 path 上調用 toString(),然后使用 string 操作查看結果。
java.nio.file 有更好的解決方案:通過在 FileSystem 對象上調用 getPathMatcher() 獲得一個 PathMatcher,然后傳入感興趣的模式。
模式
glob
glob 比較簡單,實際上功能非常強大,因此可以使用 glob 解決許多問題。
在 matcher 中,glob 表達式開頭的 **/ 表示“當前目錄及所有子目錄”,這在當你不僅僅要匹配當前目錄下特定結尾的 Path 時非常有用。
單 * 表示“任何東西”,然后是一個點,然后大括號表示一系列的可能性—-我們正在尋找以 .tmp 或 .txt 結尾的東西
regex
如果問題更復雜,可以使用 regex
文件讀寫
如果一個文件很“小”,也就是說“它運行得足夠快且占用內存小”,那么 java.nio.file.Files 類中的實用程序將幫助你輕松讀寫文本和二進制文件。
Files.readAllLines() 一次讀取整個文件(因此,“小”文件很有必要),產生一個List。
只需將 Path 傳遞給 readAllLines()
readAllLines() 有一個重載版本,包含一個 Charset 參數來存儲文件的 Unicode 編碼
Files.write() 被重載以寫入 byte 數組或任何 Iterable 對象(它也有 Charset 選項):
如果文件大小有問題怎么辦? 比如說:
文件太大,如果你一次性讀完整個文件,你可能會耗盡內存。
您只需要在文件的中途工作以獲得所需的結果,因此讀取整個文件會浪費時間。
Files.lines() 方便地將文件轉換為行的 Stream:
流式處理,跳過 13 行,然后選擇下一行并將其打印出來。
Files.lines() 對于把文件處理行的傳入流時非常有用,但是如果你想在 Stream 中讀取,處理或寫入怎么辦?這就需要稍微復雜的代碼:
因為我們在同一個塊中執行所有操作,所以這兩個文件都可以在相同的 try-with-resources 語句中打開。
PrintWriter 是一個舊式的 java.io 類,允許你“打印”到一個文件,所以它是這個應用的理想選擇
總結
雖然本章對文件和目錄操作做了相當全面的介紹,但是仍然有沒被介紹的類庫中的功能——一定要研究 java.nio.file 的 Javadocs,尤其是 java.nio.file.Files 這個類。
Java 7 和 8 對于處理文件和目錄的類庫做了大量改進。如果您剛剛開始使用 Java,那么您很幸運。在過去,它令人非常不愉快,Java 設計者以前對于文件操作不夠重視才沒做簡化。對于初學者來說這是一件很棒的事,對于教學者來說也一樣。我不明白為什么花了這么長時間來解決這個明顯的問題,但不管怎么說它被解決了,我很高興。使用文件現在很簡單,甚至很有趣,這是你以前永遠想不到的。
總結
以上是生活随笔為你收集整理的java7 uri,细数Java8中那些让人纵享丝滑的文件操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 比较源文件_Beyond Co
- 下一篇: java的字节码无法显示_【java】查