“嗨,阿米戈!”

“嗨,Bilaabo!生活怎麼樣?”

“太好了,昨天,我試著消滅了一些寄生蟲,但到目前為止我並沒有太大的成功。然後我又不得不在垃圾桶裡過夜了。”

“所以……一切還好嗎?”

“你可以那樣說。”

“好。那你今天有什麼事要給我?”

“今天我將向您介紹RandomAccessFile類。”

RandomAccessFile 等 - 1

“問題是,FileInputStream 和 FileOutputStream 將文件表示為流:您只能順序讀取和寫入它們。”

“這並不總是非常方便。有時你需要在一個文件的中間寫幾行,或者從一個數兆字節的文件的末尾讀取幾頁文本。讀取效率不是很高這些任務的整個文件。”

“創建RandomAccessFile類是為了解決這個問題。您可以使用它在文件中的任何位置寫入、讀取以及同時讀取和寫入文件。”

“多麼有趣!”

“嗯,其實挺方便的。”

“但是你如何從任意位置讀取數據呢?”

“這一切都很簡單。想像一下,你有一個文本編輯器,比如記事本,打開。它有一個光標。當你輸入一些東西時,文本會添加到光標所在的位置。讀取文件也是一樣的。閱讀從‘光標’在哪,讀/寫時,光標自動移動。”

“這裡,給你舉個例子比較好:”

讀取文件:
// r - read, the file is opened only for reading.
RandomAccessFile raf = new RandomAccessFile("input.txt", "r");

// Move the «cursor» to the 100th character.
raf.seek(100);

// Read the line starting from the current cursor position until the end of the line.
String text = raf.readLine();

// Close the file.
raf.close();

“在這個例子中,我想提請你注意兩件事:”

"首先,創建RandomAccessFile對象。第二個參數是字母 r。這意味著打開文件進行讀取(r - read)。如果要打開文件進行讀取和寫入,則必須通過 « rw » 給構造函數,而不僅僅是 « r »。”

”其次,看seek方法。你可以使用這個方法在文件中跳轉,改變當前讀/寫操作的光標位置。當一個文件第一次打開時,光標被設置為第0個字節。或者,更準確地說,在第零個字節之前。”

“我沒理解錯嗎?我們打開文件,光標在最開始——位置 0。然後我們調用seek並將光標移動到第 100 個字節。當我們調用readLine時,它從第 100 個字節開始讀取。 正確的?”

“是的。但我想提請你注意,seek 方法允許你在文件中任意跳轉。例如:”

讀取文件:
// r - read, the file is opened only for reading.
RandomAccessFile raf = new RandomAccessFile("input.txt", "r");

// The "cursor" is at the 0th character.
String text1 = raf.readLine();

// Move the "cursor" to the 100th character.
raf.seek(100);
String text2 = raf.readLine();

// Move the "cursor" to the 0th character.
raf.seek(0);
String text3 = raf.readLine();

// Close the file
raf.close();

“在這個例子中,我們首先從第 0 個字節開始讀取一行。然後我們跳到第 100 個字節並在那裡讀取一行。然後我們再次跳到第 0 個字節並讀取一行。這意味著 text1 和 text3 是相同的字符串。”

“啊。這讓事情更清楚了。”

“太好了。那麼這是另一個例子:”

讀取文件:
// rw - read/write, the file is opened for reading and writing.
RandomAccessFile raf = new RandomAccessFile("seek.txt", "rw");

// Write to the file, starting from the 0th byte.
raf.writeBytes("It is a string");

// Move the "cursor" to the 8th character.
raf.seek(8);

// Write "surprise!" to the file.
raf.writeBytes("surprise!");

// Close the file.
raf.close();

“在這裡,我們通過將 « rw »(讀/寫)傳遞給構造函數來打開文件進行讀寫。”

“然後我們將 « It is a string » 寫入文件。

“然後我們將光標移動到第 8 個字節(恰好是單詞‘string’的開頭)”

“然後我們寫 «驚喜!»”

“結果,該文件包含 «這是一個驚喜!»”

“所以,字節並沒有插入到文件的中間,而是替換了原來的字節?”

“是的。”

“如果我們將光標移動到文件的最後呢?”

“然後字節會寫到最後,文件會變大。所以這幾乎就像在文本編輯器中寫入文本一樣。”

“嗯。我想我明白了一切。你能提供RandomAccessFile類的方法的完整列表嗎?”

“喏,給你:”

方法 描述
int read() 讀取一個字節並返回它
int read(byte b[], int off, int len) 讀取字節數組
int read(byte b[]) 讀取字節數組
void readFully(byte b[]) 讀取字節數組,如果沒有足夠的字節來填充數組,則等待添加新字節
int skipBytes(int n) 跳過 n 個字節。換句話說,這會將光標向前移動 n 個字節。
void write(int b) 將一個字節寫入光標位置
void write(byte b[]) 將字節數組寫入光標位置
void write(byte b[], int off, int len) 將字節數組寫入光標位置
long getFilePointer() 返迴光標指向的字節數。它的範圍可以從 0 到文件長度
void seek(long pos) 將用於讀/寫的 «cursor» 移動到指定位置
long length() 返回文件長度
void setLength(long newLength) 設置新的文件長度。如果文件較大,則會被截斷;如果它更小,那麼它會擴展文件並用零填充新空間
void close() 關閉文件
boolean readBoolean() 從光標在文件中的當前位置讀取一個布爾值
byte readByte() 從光標在文件中的當前位置讀取一個字節
char readChar() 從光標在文件中的當前位置讀取一個字符
int readInt() 從光標在文件中的當前位置讀取一個 int
long readLong() 從光標在文件中的當前位置讀取一個 long
float readFloat() 從光標在文件中的當前位置讀取一個浮點數
double readDouble() 從光標在文件中的當前位置讀取一個 double
String readLine() 從文件中讀取一行並返回
void writeBoolean(boolean v) 將布爾值寫入文件(從光標位置開始)
void writeByte(int v) t 向文件寫入一個字節(從光標位置開始)
void writeChar(int v) 將一個字符寫入文件(從光標位置開始)
void writeInt(int v) 將一個 int 寫入文件(從光標位置開始)
void writeLong(long v) 向文件寫入一個 long(從光標位置開始)
void writeFloat(float v) 將浮點數寫入文件(從光標位置開始)
void writeDouble(double v) 向文件寫入一個雙精度值(從光標位置開始)
void writeBytes(String s) 將字符串寫入文件(從光標位置開始)
void writeChars(String s) 將字符串寫入文件(從光標位置開始)

“嗯。所以,這裡沒有什麼新東西。除了 seek()/getFilePointer() 和 length()/setLength() 方法對。”

“是的,阿米戈。一切都差不多。但這不是很方便嗎?”

“這很方便。謝謝你,Bilaabo,給了我有趣的課程和你給我的例子。”

“很高興能幫上忙,阿米戈,我的朋友!”