RandomAccessFile
RandomAccessFile類可以說是Java語言中功能最為豐富的檔案訪問類,它提供了眾多的檔案訪問方法。RandomAccessFile類支援"隨機訪問"方式,可以跳轉到檔案的任意位置處讀寫資料。要訪問一個檔案的時候,不想把檔案從頭讀到尾,而是希望像訪問一個資料庫一樣地訪問一個文字檔案,使用RandomAccessFile類是最佳選擇。
RandomAccessFile物件類中有個位置指示器,指向當前讀寫處的位置,當讀寫n個位元組後,檔案指示器將指向這n個位元組後的下一個位元組處。剛開啟檔案時,檔案指示器指向檔案的開頭處,可以移動檔案指示器到新的位置,隨後的讀寫將從新的位置開始。
RandomAccessFile類在檔案隨機(相對於順序)讀取時有很大的優勢,但該類僅限於操作檔案,不能訪問其他得IO裝置,如網路、記憶體映像等。
RandomAccessFile構造方法
RandomAccessFile類為使用者提供了兩種構造方法:
1、RandomAccessFile(File file, String mode)
2、RandomAccessFile(String name, String mode)
其實第二種構造方法也是new一個File出來再呼叫第一種構造方法,建議使用第一種構造方法,因為第一篇文章就說了File是IO的基礎,有一個File不僅僅可以通過RandomAccessFile對檔案進行操作,也可以通過File物件對檔案進行操作。至於mode,Java給開發者提供了四種mode:
模 式 | 作 用 |
r | 表示以只讀方式開啟,呼叫結果物件的任何write方法都將導致丟擲IOException |
rw | 開啟以便讀取和寫入,如果該檔案尚不存在,則嘗試建立該檔案 |
rws | 開啟以便讀取和寫入,相對於"rw",還要求對檔案內容或後設資料的每個更新都同步寫入到底層儲存裝置 |
rwd | 開啟以便讀取和寫入,相對於"rw",還要求對檔案內容的每個更新都同步寫入到底層儲存裝置 |
注意第二點"rw"模式,對rw模式的解釋意味著Java並不強求指定的路徑下一定存在某個檔案,假如檔案不存在,會自動建立
RandomAccessFile中的方法
RandomAccessFile中有如下一些常用方法:
方 法 | 作 用 |
void close() | 重要,關閉此隨機訪問檔案流並釋放與該流關聯的所有系統資源 |
FileChannel getChannel() | 返回與此檔案關聯的唯一FileChannel物件,NIO用到 |
long getFilePointer() | 返回此檔案中的當前偏移量 |
long length() | 返回此檔案的長度 |
int read() | 從此檔案中讀取一個資料位元組 |
int read(byte[] b) | 將最多b.length個資料位元組從此檔案讀入byte陣列,返回讀入的總位元組數,如果由於已經達到檔案末尾而不再有資料,則返回-1。在至少一個輸入位元組可用前,此方法一直阻塞 |
int read(byte[] b, int off, int len) | 將最多len個資料位元組從此檔案的指定初始偏移量off讀入byte陣列 |
boolean readBoolean() | 從此檔案讀取一個boolean,其餘readByte()、readChar()、readDouble()等類似 |
String readLine() | 從此檔案讀取文字的下一行 |
void seek(long pos) | 重要,設定到此檔案開頭測量到的檔案指標偏移量,在該位置發生下一個讀取或寫入操作 |
int skipBytes(int n) | 重要,嘗試跳過輸入的n個位元組以丟棄跳過的位元組,返回跳過的位元組數 |
void write(byte[] b) | 將b.length個位元組從指定byte陣列寫入到此檔案中 |
void write(byte[] b, int off, int len) | 將len個位元組從指定byte陣列寫入到此檔案,並從偏移量off處開始 |
void write(int b) | 向此檔案寫入指定的位元組 |
void writeBoolean(boolean v) | 按單位元組值將boolean寫入該檔案,其餘writeByte(int v)、writeBytes(String s)、writeChar(int v)等都類似 |
RandomAccessFile使用例項
先定義一個實體類:
public class Employee { private String name; private int age; private final static int LEN = 8; public Employee() { } public Employee(String name, int age) { if (name.length() > LEN) { name = name.substring(0, 8); } else { while (name.length() < LEN) { name = name + "\u0000"; } } this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
第一部分,寫檔案,該檔案在路徑下並沒有,所以Java會自動幫我們建立:
Employee e1 = new Employee("zhangsan", 23); Employee e2 = new Employee("lisi", 24); Employee e3 = new Employee("wangwu", 25); RandomAccessFile raf0 = new RandomAccessFile("D:/employee.txt", "rw"); raf0.writeBytes(e1.getName()); raf0.writeInt(e1.getAge()); raf0.writeBytes(e2.getName()); raf0.writeInt(e2.getAge()); raf0.writeBytes(e3.getName()); raf0.writeInt(e3.getAge()); raf0.close();
檔案建立好了,D盤下也有該檔案了,所以讀取一下,這裡使用了一些小技巧來演示seek方法和skipBytes方法:
RandomAccessFile raf1 = new RandomAccessFile("D:/employee.txt", "r"); int len = 8; raf1.skipBytes(12); // 跳過第一個員工的資訊,其姓名8位元組,年齡4位元組 System.out.println("第二個員工的資訊:"); String str = ""; for (int i = 0; i < len; i++) { str = str + (char)raf1.readByte(); } System.out.println("name:" + str); System.out.println("age:" + raf1.readInt()); System.out.println("第一個員工的資訊:"); raf1.seek(0); str = ""; for (int i = 0; i < len; i++) { str = str + (char)raf1.readByte(); } System.out.println("name:" + str); System.out.println("age:" + raf1.readInt()); System.out.println("第三個員工的資訊:"); raf1.skipBytes(12); // 跳過第二個員工的資訊 str = ""; for (int i = 0; i < len; i++) { str = str + (char)raf1.readByte(); } System.out.println("name:" + str.trim()); System.out.println("age:" + raf1.readInt()); raf1.close();
看一下執行結果:
第二個員工的資訊: name:lisi age:24 第一個員工資訊: name:zhangsan age:23 第三個員工資訊: name:wangwu age:25
可能有人奇怪,"zhangsan"加上一個int跳過12個位元組可以理解,但是"lisi"、"wangwu"為什麼加上int要跳過12個位元組呢?明明"lisi"只有4個位元組,"wangwu"只有6個位元組啊。這個就涉及到一個"位元組對齊"的問題了,有興趣的可以瞭解一下。另外,再說一下,RandomAccessFile使用完一定要及時close()。