RandomAccessFile 既可以读取文件也可以写入文件。
RandomAccessFile 最大的优势就是随机访问,能够直接跳转到任意位置进行文件的读写操作。
适用场景
如果需要访问文件的部分内容,而不是把文件从头读到尾,RandomAccessFile是最好的选择,因此RandomAccessFile经常用在网络请求中的多线程下载以及断点续传。
RandomAccessFile的创建方式有以下两种:
RandomAccessFile(File file,String mode);
RandomAccessFile(String fileName,String mode);
第一个参数是需要访问的文件
第二个参数是访问文件时的模式,主要为下面两个
RandomAccessFile提供了一个可以从文件中读取字节的方法:
int read()
该方法会从RandomAccessFile当前指针位置读取一个byte(8位) 填充到int的低八位, 高24位为0, 返回值范围正数: 0~255, 如果返回-1表示读取到了文件末尾EOF(EOF:End Of File)! 每次读取后自动移动文件指针, 准备下次读取。
RandomAccessFile提供了一个可以从文件中批量读取字节的方法:
int read(byte[] b)
该方法会从文件中尝试最多读取给定数组的总长度的字节量,并从给定的字节数组第一个位置开始,将读取到的字节顺序存放至数组中,返回值为实际读取到的字节量 。
RandomAccessFile提供了一个可以向文件中写出字节的方法:
void write(int d)
该方法会根据当前指针所在位置处写入一个字节,是将参数int的”低8位”写出。
RandomAccessFile提供了一个可以向文件中写出一组字节的方法:
void write(byte[] d)
该方法会根据当前指针所在位置处连续写出给定数组中的所有字节,与该方法相似的还有一个常用方法:
void write(byte[] d,int offset,int len)
该方法会根据当前指针所在位置处连续写出给定数组中的部分字节,这个部分是从数组的offset处开始,连续len个字节。
offset + len < 数组的长度
RandomAccessFile在对文件访问的操作全部结束后,要调用close()方法来释放与其关联的所有系统资源。
void close()
将文件指针定位到pos的位置
void seek(long pos)
long getFilePointer()
首先来看一下随机读写文件的存储方式,用RandomAccessFile类写入的数据一般都是按照ASCII字符的形式保存在文件 中,即以字节的形式存储,字节是计算机存储设备贬值的最小单元。
当我们按照顺讯存放时,读取的时候也要按顺序读取,不然会报错。
如下图就是依次写入int,byte,double,int,byte,short数据时,在文件中的存放。文件指针会随着数据的写入按照写入后移。
特殊的数据读取
字符串读readUTF()和字符串写writeUTF(String str)
RandomAccessFile access = new RandomAccessFile("test", "rw");
access.writeUTF("你好"); //以utf-8格式写入数据
access.writeUTF("朋友"); //以utf-8格式写入数据
access.close();
access = new RandomAccessFile("test", "r");
System.out.println(access.readUTF());
System.out.println(access.readUTF());
access.close();
文档中的结构
可以看到在汉字的前面有一些字符,其实就是2个字节的标记字符串长度的ASCII编码
按顺序跳过内容,进行输出
RandomAccessFile raf = new RandomAccessFile("fileTest", "rw");
raf.writeInt(20);
raf.writeShort(888);
raf.writeUTF("张三李四");
raf.close();
raf = new RandomAccessFile("fileTest", "r");
System.out.println(raf.readInt());
raf.skipBytes(2);
// System.out.println(raf.readShort());
System.out.println(raf.readUTF());
raf.close();
//结果
20
张三李四
多线程文件操作
注意这里使用的是write(string.getBytes)进行写入。
public static void thread() throws IOException {
RandomAccessFile raf = new RandomAccessFile("E://abc.txt", "rw");
raf.setLength(1024 * 1024);
raf.close();
String s1 = "我是第一个字符串";
String s2 = "我是第二个字符串";
String s3 = "the third String";
String s4 = "the fourth string";
String s5 = "the fifth string";
//利用多线程同时写入一个文件
new FileWriteThread(10 * 1, s1.getBytes()).start();
new FileWriteThread(20 * 2, s2.getBytes()).start();
new FileWriteThread(30 * 3, s3.getBytes()).start();
new FileWriteThread(40 * 4, s4.getBytes()).start();
new FileWriteThread(50 * 5, s5.getBytes()).start();
}
static class FileWriteThread extends Thread {
private int skip;
private byte[] content;
public FileWriteThread(int skip, byte[] content) {
this.skip = skip;
this.content = content;
}
public void run() {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile("E://sb.txt", "rw");
raf.seek(skip);
raf.write(content);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
如果文件中已存在中文字符,然后seek选择的位置刚好会覆盖部分中文字符串,那么就会出现乱码现象。
读取文件中的内容,为了编码中文乱码出现,需要将编码转换成utf-8编码
RandomAccessFile file = new RandomAccessFile("E://sb.txt", "r");
//在Windows下raf会默认编码成8859_1
String line = null;
while ((line = file.readLine()) != null) {
System.out.println(new String(line.getBytes("ISO-8859-1"), "utf-8"));
}
总结
RandomAccessFile 使用非常简单,主要应用于随机读写。
我们需要重点关注的内容就是 RandomAccessFile是以字节的形式进行存储的,所以对于中文字符串的读写要非常注意编码方式,尽量避免乱码现象。