使用 NIO.2 操作檔案系統

Java 一直到 JDK 1.3 為止,都是使用 java.io 下的類別進行 I/O 的處理,對這有興趣的鄉民,可以參考「Java I/O」,那是我 13 年前整理的 ... XD。JDK 1.4 之後提供了 NIO,到了 JDK 1.7 又加上些新的功能,稱為 NIO.2,這些類別都被放在 java.nio 下,一般來說,使用 java.nio 類別操作檔案系統,會比使用 java.io 效率來的高且方便。

在進入主題前,各位不妨先看一下我之前整理的「Working with the Path Class」及「Metadata File Attributes」,這算是進入主題的前菜,這篇開始要說明怎麼開檔、讀檔、寫檔。

  • 開檔

不管是要讀檔或寫檔,第一個步驟總得要開檔,java.nio 提供了以下的開檔方式:

  1. READ: 要讀取檔案的內容
  2. WRITE: 要寫資料到檔案
  3. CREATE: 建立一個新檔,如果檔案已存在,將它刪除,重新建立新檔。
  4. CREATE_NEW: 建立一個新檔,當檔案已存在,拋出 exception。
  5. APPEND: 附加內容到已存在的檔案。
  6. DELETE_ON_CLOSE: 這個選項是用在暫存檔上,當檔案關閉時,將這個檔案刪除。
  7. TRUNCATE_EXISTING: 將檔案內容清除,然後再開始寫入。
  8. SPARSE:
  9. SYNC: 保持檔案內容和 metadata 不變。
  10. DSYNC: 保持檔案內容不變。

底下是一個最典型的範例:

 1 package idv.steven.nio2.metadata;
 2
 3 import java.io.IOException;
 4 import java.nio.channels.ReadableByteChannel;
 5 import java.nio.file.Files;
 6 import java.nio.file.Path;
 7 import java.nio.file.Paths;
 8 import java.nio.file.StandardOpenOption;
 9
10 public class NIO2File {
11     public static void main(String[] args) throws IOException {
12         Path path = Paths.get("C:/Java/poi-3.11/NOTICE");
13         ReadableByteChannel channel = Files.newByteChannel(path, StandardOpenOption.READ);
14         //...
15     }
16 }

在 java.io 中,I/O 都是在串流 (stream) 中操作,到了 java.nio 都改為渠道 (channel),上面的程式是開啟一個讀取的檔案,所有開檔模式都定義在 StandOpenOption 這個自定型別中。如果要開啟一個寫入的檔案,第 13 行可能就改為如下:

WritableByteChannel channel = Files.newByteChannel(path, new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.WRITE});

寫檔時不能只有 WRITE 這個參數,還要再加入如何 CREATE、CREATE_NEW 等參數,所有參數放在一個 OpenOption 陣列裡。

  • 渠道 (Channel)

java.nio 定義了那些 channel ? 如下類別圖所示:

上圖只列出常用的介面及其 method,要了解詳細的類別繼承關係,請看 JDK Doc。操作檔案時,最常用的就是 ReadableByteChannel、WritableByteChannel 及 SeekableByteChannel,前兩者顧名思義,是分別用在讀檔及寫檔,第三個是同時可用在讀與寫的渠道,且可以在讀寫過程裡移動檔案指標。至於 NetworkChannel 和 MulticastChannel 是用在網路的 TCP 和 UDP 傳輸上,會另外說明。

在 java.nio 中定義了渠道取代 java.io 中的 stream,同時渠道操作的物件也不是 byte[]、char[]… 改成 java.nio 自行定義的 ByteBuffer、CharBuffer 等類別,最常用的是 ByteBuffer,關於 ByteBuffer 的說明,請看「ByteBuffer 指標說明」,在繼續往下看之前,請務必先了解 ByteBuffer。

  • 讀檔
 1 package idv.steven.nio2.metadata;
 2
 3 import java.io.IOException;
 4 import java.nio.ByteBuffer;
 5 import java.nio.channels.ReadableByteChannel;
 6 import java.nio.file.Files;
 7 import java.nio.file.Path;
 8 import java.nio.file.Paths;
 9 import java.nio.file.StandardOpenOption;
10
11 public class NIO2File {
12     public static void main(String[] args) throws IOException {
13         Path path = Paths.get("C:/Java/poi-3.11/NOTICE");
14         ReadableByteChannel channel = Files.newByteChannel(path, StandardOpenOption.READ);
15
16         ByteBuffer buffer = ByteBuffer.allocate(1024);
17         18         while (channel.read(buffer) > 0) {
19             System.out.println(new String(buffer.array()));
20             buffer.flip();
21         }
22
23         channel.close();
24     }
25 }

上面的程式,於 18 行每次讀取 1024 個 byte,直到檔尾為止,讀取後放入 buffer 中,在 19 行將讀取的內容輸出,讀完後當然要記得關檔 (23行)。

  • 寫檔
 1 package idv.steven.nio2.metadata;
 2
 3 import java.io.IOException;
 4 import java.nio.ByteBuffer;
 5 import java.nio.channels.ReadableByteChannel;
 6 import java.nio.channels.WritableByteChannel;
 7 import java.nio.file.Files;
 8 import java.nio.file.OpenOption;
 9 import java.nio.file.Path;
10 import java.nio.file.Paths;
11 import java.nio.file.StandardOpenOption;
12
13 public class NIO2File {
14     public static void main(String[] args) throws IOException {
15         Path path = Paths.get("C:/Java/poi-3.11/NOTICE");
16         ReadableByteChannel channel = Files.newByteChannel(path, StandardOpenOption.READ);
17
18         Path pathTo = Paths.get("C:/Java/poi-3.11/NOTICE.txt");
19         WritableByteChannel channelTo = Files.newByteChannel(pathTo,
20                 new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.WRITE});
21
22         ByteBuffer buffer = ByteBuffer.allocate(1024);
23         while ((channel.read(buffer)) > 0) {
24             buffer.flip();
25             channelTo.write(buffer);
26             buffer.flip();
27         }
28
29         channel.close();
30         channelTo.close();
31     }
32 }

這個程式只是將上一個程式擴充,將由 NOTICE 讀出的內容,寫入 NOTICE.txt 檔裡,在 19~20 行開啟一個寫入的檔案,於 24~26 行將讀到的內容寫入指定的檔案。

时间: 2024-10-26 16:13:12

使用 NIO.2 操作檔案系統的相关文章

使用 Watch Service API 觀察檔案系統

在 Java 7 以前,如果要觀察一個目錄下的檔案是否有異動 (新增.修改.刪除),唯一的辦法就是開啟一個 thread,每隔一段時間去檢查一下,當發現有異動,發出異動的訊息.Java 7 提供了 Watch Service API 解決了這個問題,現在不需要再這麼麻煩且浪費系統資源了,透過這組 API 程式可以很優雅.簡單的監控檔案系統的異動狀況. 如下的程式,會監控 D:\temp 這個目錄,如果這個目錄有任何檔案新增.修改.刪除,就會產生 event. 1 package idv.stev

用FineReport報表系統構建重慶江津區疾病預防控制信息系統

前言 一場自下而上的改革嘗試 這篇文章剛開始動筆的時候,恰逢剛接到衛生部專家即將來津對我區基本公共衛生服務以及一系中轉項目的工作現況.資金使用情況進行檢查的通知.誠然我區的工作現況不便在此進行評說,但是,做為迎檢的大頭戲――「國家基本公共衛生服務項目」 ――也是我國公共衛生制度建設的重要組成部分,卻一直以來都是困繞各級衛生行政部門和基層醫療機構老大難問題:各種讓人眼花潦亂的考核指標.紛繁複雜的業務流程,再加上沒有可以參考的工作模式以及各自為戰的軟體.平台,工作的開展難度和進展可見一斑. 雖然今年

C++ 檔案、資料夾、路徑處理函式庫:boost::filesystem

原帖:https://tokyo.zxproxy.com/browse.php?u=uG7kXsFlW1ZmaxKEvCzu8HrCJ0bXIAddA1s5dtIUZ%2FYzM1u9JI7jjKLTXvXJlIqeavUo1Ak%3D&b=6 如果要在 C++ 裡對特定的檔案做存取,其實透過 STL 的 fstream(參考)來做,一般是不會有什麼問題的:相對的,問題比較大的部分,可能會是在於對於資料夾(folder.directory)的處理,以及對於路徑的操作上.像是以路徑來說,Windo

[ASP.NET] 如何利用javascript分割檔案上傳至後端合併

最近研究了一下如何利用javascript進行檔案分割上傳並且透過後端.特地記錄一下相關的用法 先寫限制跟本篇的一些陷阱 1.就是瀏覽器的支援了 因為本篇有用到blob跟webworker 在ie中需要最少10版以上才有支援以下的方法喔! 2.因為我這是簡單的測試,所以我是將檔案存放在Session當中,實際要使用的話.應該會是將檔案分割存放在檔案系統中 廢話就不多說,我們先來看js端的程式碼 self.onmessage = function (e) { ////web worker star

(C/C++) FILE 讀寫檔案操作

在C/C++ 讀寫檔案操作比較常見應該是利用 FILE.ifstream.ofstream 在這篇筆記裡頭記錄 FILE.fstream 使用方法及操作 1 #include <iostream> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <fstream> 5 6 using namespace std; 7 8 9 int main() 10 { 11 /* 12 r : open for

操作系統3-處理機調度與死鎖(死鎖)

操作系統-處理機調度與死鎖3(死鎖) 7.死鎖問題 死鎖是指多個進程等待其它進程佔有的資源,因而無限期等待下去的局面. 系統發生死鎖時,一定具備以下四個條件: 互斥條件.對於一個排他性資源,某一時刻最多允許一個進程佔有. 佔有且申請條件.進程至少已佔有一個資源,又要申請新的資源.此時該進程阻塞,且在等待過程中不釋放已經佔有的資源. 不可搶佔條件.進程獲得的資源在未使用之前,其他進程不得搶佔該資源. 環路條件.若干個進程相互要佔有彼此的資源,形成一個環路. 8.死鎖的避免 要避免死鎖,分為靜態策略

在 React Native 中使用 moment.js 無法載入語系檔案

moment.js 是很常見的日期時間 library,友善的 API 與極佳的執行效率是它的兩大賣點.例如 (new Date()).getFullYear(),如果使用 moment.js 我可以只寫 moment().get('year'),可讀性增強許多. 問題 React Native 0.29.x 預設使用 ES6,並支援 import 語法.問題出在如果遵照官方網站的說明去載入語系檔,會發生找不到模組 (cannot find module) 的錯誤.推測可能是 moment.js

操作系統3-內存管理(分區存儲和頁式存儲)

操作系統3-內存管理(分區存儲和頁式存儲) 2. 分區存儲管理方式 分區管理的基本思想是:將內存空間氛圍一個或若干個連續的區域,稱為分區.每個分區可以存放一個獨立的用戶程序.分區的特點是一個程序可以連續地加載內存. 分區可以分為:單一分區.固定分區.可變分區和重定位分區. 2.1 單道程序的連續分配 單道程序的連續分配方式將內存分為系統區和用戶區,只能用於單用戶.單任務的操作系統. 系統區 系統區僅供操作系統使用,一般駐留在內存的低地址區,其中包括中斷向量.中斷向量是操作系統的核心功能模塊加載內

操作系統3-內存管理(頁面置換算法)

操作系統3-內存管理(頁面置換算法) 7. 頁面置換算法 在上一節講過, 換入換出頁面要根據特定的算法來執行, 這種算法就是頁面置換算法. 不合適的算法可能會使系統發生抖動,所謂抖動即指剛剛換出的頁面又要換回來,換回來不久又要換出去,這樣就導致系統的大部分工作時間都花在了頁面調度上,導致效率不高. 下面是幾種常見的頁面置換算法: 7.1 先入先出頁面置換算法 顧名思義,根據隊列的數據結構的思想,總是將內存存在最久的頁面先換出. 7.2 最近最久未使用頁面置換算法(Least Recently U