“嗨,阿米戈!”
“嗨,Bilaabo!生活怎么样?”
“太好了,昨天,我试着消灭了一些寄生虫,但到目前为止我并没有太大的成功。然后我又不得不在垃圾桶里过夜了。”
“所以……一切还好吗?”
“你可以那样说。”
“好。那你今天有什么事要给我?”
“今天我将向您介绍RandomAccessFile类。”
“问题是,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,给了我有趣的课程和你给我的例子。”
“很高兴能帮上忙,阿米戈,我的朋友!”
GO TO FULL VERSION