介绍
是JAVA I/O流体系中功能最丰富的文件内容访问类,它提供了众多方法来访问文件内容。该类的主要目的是提供为读写文件提供更高的可操作性。
RandomAccessFile是Java输入/输出流体系中功能最丰富的文件内容访问类,既可以读取文件内容,也可以向文件输出数据。与普通的输入/输出流不同的是,RandomAccessFile支持跳到文件任意位置读写数据,RandomAccessFile对象包含一个记录指针,用以标识当前读写处的位置,当程序创建一个新的RandomAccessFile对象时,该对象的文件记录指针对于文件头(也就是0处),当读写n个字节后,文件记录指针将会向后移动n个字节。除此之外,RandomAccessFile可以自由移动该记录指针,来获取指定位置的数据的目的。当然也可以从指定的位置开始插入新的数据
RandomAccessFile在jdk中的情况如下图:
构造函数
RandomAccessFile有两个构造函数:
public RandomAccessFile(String name, String mode)
public RandomAccessFile(Flie fileName, String mode)
第一个构造方法最终调用的还是第二个构造方法。构造方法中的以一个参数是文件所在的路径或File对象,第二个参数mode是关于文件的权限的。该参数指定RandomAccessFile的访问模式,该参数有如下四个值:
r:以只读方式打开指定文件。如果试图对该RandomAccessFile指定的文件执行写入方法则会抛出IOException。
rw:以读取、写入方式打开指定文件。如果该文件不存在,则尝试创建文件。
rws:以读取、写入方式打开指定文件。相对于rw模式,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备,默认情形下(rw模式下),是使用buffer的,只有cache满的或者使用RandomAccessFile.close()关闭流的时候儿才真正的写到文件。
rwd:与rws类似,只是仅对文件的内容同步更新到磁盘,而不修改文件的元数据。
主要方法介绍
RandomAccessFile的功能实现主要是依靠三个函数:
public void seek(long pos) : 将文件记录指针重新指向pos的位置。可以实现从该位置开始读取和插入数据。
public long getFilePointer(): 获取当前文件指针的位置。
public long getLength(): 获取当前文件的内容长度。
public native setLength(long newLength): 设置当前文件内容的长度。当文件已有的内容长度超过newLength时,超过的部分将会被截断。使文件长度达到newLength。同时getFilePointer()的值若小于newLength,则不变。如大于,则返回newLength。
RandomAccessFile的读写操作。和InputSream和OutPutStream相似。支持各种类型数据的写入,但是写入方法和读出方法是一一对应的。用什么方法写入就需要用什么方法读出,否则会出现乱码的情况。RandomAccessFile是自己可以实现读和写。且在同一个流的操作中读和写共用一个文件记录指针。
以下是在使用过程中的测试:
public class RandomAccessFileTest {
static String path = "static/test.txt";
@Test
public void test(){
try {
RandomAccessFile randomAccessFile = new RandomAccessFile(this.getClass().getClassLoader().getResource(path).getPath(), "rw");
RandomAccessFile randomAccessFile1 = new RandomAccessFile(this.getClass().getClassLoader().getResource(path).getPath(), "rw");
//二进制
// bytesRW(randomAccessFile, randomAccessFile1);
//字符串
stringRW(randomAccessFile,randomAccessFile1);
}catch (Exception e){
e.printStackTrace();
}
}
我在测试中只测试了二进制和字符串的写入和读出。
首先是二进制
/*
二进制操作
*/
private void bytesRW(RandomAccessFile randomAccessFile, RandomAccessFile randomAccessFile1){
try {
//二进制数写入,每一次都是默认在已有的数据后面追加
randomAccessFile.write(0);
byte[] b = new byte[]{1,2,3,4,5,6};
randomAccessFile.write(b);
randomAccessFile.write(b, 2, 2);
randomAccessFile.close();
/*第一次测试
//二进制读数据,每一次读都会被记录下来,文件未被close之前,再一次读操作都会从上一次的记录之后开始读数据。超过则会读取不到数据 */
byte[] a = new byte[2];
randomAccessFile1.read(a); //返回 a = 0,1.
byte[] c = new byte[50];
randomAccessFile1.read(c); //返回 c = 2,3,4,5,6,3,4,之后全是0
randomAccessFile1.seek(1);
byte[] a = new byte[2];
randomAccessFile1.read(a); // seek 之后 变为 1,2
byte[] c = new byte[50];
randomAccessFile1.read(c); //seek 之后 变为 3,4,5,6,3,4
randomAccessFile1.close();
/* 第二次测试
//对同一个文件的再一次操作,原来的数据被覆盖,但不是全部覆盖,数据会从头开始写入,只覆盖掉被写入的数据的长度,之后的数据不变
RandomAccessFile randomAccessFile2 = new RandomAccessFile(this.getClass().getClassLoader().getResource(path).getPath(), "rw");
RandomAccessFile randomAccessFile3 = new RandomAccessFile(this.getClass().getClassLoader().getResource(path).getPath(), "rw");
byte[] e = new byte[]{11,12,13};
randomAccessFile2.write(e); //原来是0 1 2 3 4 5 6 3 4 ,写入之后变成 11 12 13 4 5 6 3 4
randomAccessFile2.close();
byte[] d = new byte[50];
randomAccessFile3.read(d);
randomAccessFile3.close();
*/
//使用seek可以实现追加数据
/*RandomAccessFile randomAccessFile2 = new RandomAccessFile(this.getClass().getClassLoader().getResource(path).getPath(), "rw");
RandomAccessFile randomAccessFile3 = new RandomAccessFile(this.getClass().getClassLoader().getResource(path).getPath(), "rw");
randomAccessFile2.seek(randomAccessFile2.length());
byte[] e = new byte[]{11,12,13};
randomAccessFile2.write(e); //原来是0 1 2 3 4 5 6 3 4 ,写入之后变成 11 12 13 4 5 6 3 4
randomAccessFile2.close();
byte[] d = new byte[50];
randomAccessFile3.read(d);
randomAccessFile3.close();*/
/*
getFilePointer() : 记录上一次操作之后,文件记录指针的位置
*/
RandomAccessFile randomAccessFile2 = new RandomAccessFile(this.getClass().getClassLoader().getResource(path).getPath(), "rw");
RandomAccessFile randomAccessFile3 = new RandomAccessFile(this.getClass().getClassLoader().getResource(path).getPath(), "rw");
randomAccessFile2.getFilePointer(); //0 没有操作所以是0
//randomAccessFile2.seek(randomAccessFile2.length());
byte[] e = new byte[]{11,12,13};
randomAccessFile2.write(e); //原来是0 1 2 3 4 5 6 3 4 ,写入之后变成 11 12 13 4 5 6 3 4
randomAccessFile2.getFilePointer(); //3 记录是操作之后文件记录指针的位置,添加了三位数,所以是3
randomAccessFile2.close();
byte[] d = new byte[5];
randomAccessFile3.read(d);
randomAccessFile3.getFilePointer(); //5 记录是操作之后文件记录指针的位置,读取了5位数,所以是5
randomAccessFile3.close();
} catch (Exception e) {
e.printStackTrace();
}
}
我一共做了三次测试。
第一次测试中,可以看出,RandomAccessFile 写入时,只要是同一个流操作,都是在上一次插入的数据之后接着插入,不会覆盖之前插入的数据。在读数据的时候。默认从文件开始处(文件记录指针为0)开始读数据,每一次读出数据之后文件记录指针就会指向该位置,下一次同一个流的读取或写入都会从该位置开始。
第二次测试中,利用新的流对第一次插入的数据的文件进行操作。当写入时就会发现。新的数据会覆盖原来的数据。但是这个不覆盖不是全部覆盖。只是仅仅覆盖掉自己需要写入的数据的长度。至于多出来的部分依旧保持不变。
第三次测试是 seek(pos) 方法和 getFilePointer() 方法。前置是将该对象的文件记录指针指向文件中pos的位置。后者是获取当前流的文件记录指针的位置。seek(pos)可以实现在文件的任意指定位置插入数据。
以下是对字符串的操作。就不在重复说明了。
/*
字符串获取测试
*/
private void stringRW(RandomAccessFile randomAccessFile, RandomAccessFile randomAccessFile1){
String test = "白日依山尽";
try {
randomAccessFile.write("白日依山尽".getBytes(StandardCharsets.UTF_8));
randomAccessFile.getFilePointer();
//randomAccessFile.seek(12);
randomAccessFile.write("\r\n黄河入海流".getBytes(StandardCharsets.UTF_8));
/* byte[] c = new byte[1024];
randomAccessFile.seek(0); 测试读写是不是共用同一个文件指针,结果显示是的
randomAccessFile.read(c);
System.out.println(new String(c,StandardCharsets.UTF_8));*/
randomAccessFile.close();
//String result = randomAccess;
// File1.readUTF();
byte[] b = new byte[1024];
randomAccessFile1.read(b);
System.out.println(new String(b,StandardCharsets.UTF_8));
//System.out.println(randomAccessFile1.readUTF());
randomAccessFile1.close();
} catch (Exception e){
e.printStackTrace();
}
}
}
有任何错误和不当之处,请指正,谢谢。
以下是参考处:
https://blog.csdn.net/qq_40208605/article/details/80647803
https://www.cnblogs.com/dongguacai/p/5699444.html
https://www.cnblogs.com/baoliyan/p/6225842.html