“嗨,阿米戈!”

“嗨,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,给了我有趣的课程和你给我的例子。”

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