前几篇文章讲的是File以及相关流的使用,这节主要介绍随机访问文件的使用。
随机访问文件,如其名:使用它能够对文件进行一系列的随机操作。
1.RandomAccessFile基本介绍
看看它的类继承结构吧:
可以看出它继承了Object类以及实现了DataOutput, DataInput, Closeable
三个接口,我们可以看出它并没有继承自IO流体系中的任何一个,但是它本身使用的是字节流
。
RandomAccessFile
的主要功能:对文件进行随机访问/读取/写入,可指定任意的写入/读取位置
。RandomAccessFile
的特性:它属于字节流,可以读/写字节和所有原始类型
的值;它具有文件指针
,根据文件指针的位置进行读/写操作的。读写的同时,指针跟随移动。文件指针是我们·下一次读取或写入将开始的光标·。其值指示光标与文件开头的距离(以字节为单位)。
它的构造方法与IO体系非常的相似,第一个参数传入FilePath或者一个File
即可,不同之处在于,它具有四个不同的模式
,我们更具自己的需求实例化出相应模式的RandomAccessFile
即可,模式如下表:(后两种不知道啥区别,知道的大佬们,给些链接。。。)
模式 | 含义 |
---|---|
“r” | 以只读的方式打开 |
“rw” | 以读/写的方式打开,如果文件不存在,则自动创建 |
“rws” | 该文件以读写模式打开。 对文件的内容及其元数据的任何修改立即被写入存储设备 |
“rwd” | 该文件以读写模式打开。 对文件内容的任何修改立即写入存储设备。 |
RandomAccessFile
常用的方法:
seek(long n)
–将文件指针设置在文件中的特定位置skipBytes(long n)
–跳过n个字节getFilePointer()
– 获得文件指针length()
– 获取文件长度,我们可以通过使用其setLength()
方法来扩展或截断文件。writeUTF()
–写入一个字符串writeInt()/writeBoolean()/writeDouble()
–写入原始类型
的数据,不一一列出,详见API;相对应的一系列readXx()
方法即读取;与IO体系中类似。
2.RandomAccessFile的使用
话不多说,直接上自己照着常用案例写的demo:
import java.io.*;
import java.net.URLEncoder;
//RandomAccessFile 本身是字节流
//seek()方法将文件指针设置在文件中的特定位置。
//skipBytes(long n)跳过n个字节
//getFilePointer() 获得文件指针
//length() 获取文件长度
//RandomAccessFile的length()方法返回文件的当前长度。我们可以通过使用其setLength()方法来扩展或截断文件。
//一系列的writeX()方法
//TODO 待解决 中文乱码
//四种模式
//"r" :只读模式
//"rw" : 读写模式,文件不存在时,会自动创建
//"rws" :读写模式,对文件的内容以及其元数据的任何修改立即被写入存储设备
//"rwd" : 读写模式,对文件内容的任何修改立即被写入存储设备
public class RandomAccessFileTest {
static final String filePath = "G:" + File.separator + "io_test" + File.separator + "io_test.txt";
static int count = 0;
static String str = "random_access_file/测试";
public static void main(String[] args) {
//System.out.println(readOnlyFile());
//wrMode();
try {
RandomAccessFile raf = new RandomAccessFile("G:"+File.separator+"random_access_file.txt","rw");
//写入一个int类型的数据
// raf.writeInt(count);
//raf.writeDouble(5.6);
// raf.write("你好?aa".getBytes());
//raf.writeUTF(str);
//int value = raf.readInt();
//System.out.println(value);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//只读模式下的读取
public static String readOnlyFile() {
//
RandomAccessFile raFileReadOnly = null;
StringBuilder builder = new StringBuilder();
try {
//创建一个 只读的随机访问
raFileReadOnly = new RandomAccessFile(filePath,
"r");
byte[] bytes = new byte[1024];
int len;
while ((len = raFileReadOnly.read(bytes)) != -1) {
builder.append(new String(bytes, 0, len, "gbk"));
}
//只读模式下只能读取,写入报错 拒绝访问
// raFileReadOnly.write('c');
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (raFileReadOnly != null)
raFileReadOnly.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return builder.toString();
}
//读写模式
public static void wrMode() {
RandomAccessFile raFileRw = null;
//StringBuilder builder = new StringBuilder();
try {
raFileRw = new RandomAccessFile(filePath, "rw");
long len = raFileRw.length();
raFileRw.skipBytes((int) len);
raFileRw.writeUTF("测试一");
long point = raFileRw.getFilePointer();
System.out.println("point:" + point + "len:" + len);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raFileRw != null) {
try {
raFileRw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
无在文件的任意位置插入的实现:实现原理,将文件指针指向要插入的位置,将指针之后的内容存在缓冲,插入后追加到原文件。
//pos 代表要插入的光标位置
//str 要插入的值
public static void insert(long pos, String str) {
FileOutputStream fos = null;
FileInputStream fis = null;
try {
RandomAccessFile raf
= new RandomAccessFile("G:" + File.separator + "random_access_file.txt",
"rw");
//创建临时空文件
File tempFile = File.createTempFile("temp", null);
//在虚拟机终止时,请求删除此抽象路径名表示的文件或目录
tempFile.deleteOnExit();
//将光标移至要插入数据的位置
raf.seek(pos);
byte[] bytes = new byte[1024];
int len;
fos = new FileOutputStream(tempFile);
fis = new FileInputStream(tempFile);
//将光标后的数据写入临时文件
while ((len = raf.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.flush();
//将光标移至要插入数据的位置,因为上面将插入位置之后的数据全部写入临时文件,光标为文件长度,需要重新定位
raf.seek(pos);
//插入内容
raf.writeUTF(str);
//再次追加原先的内容
byte b[] = new byte[1024];
int len1;
while ((len1 = fis.read(b)) != -1) {
raf.write(b, 0, len1);
}
raf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
close(fos);
close(fis);
}
}
注意:包含基本API的使用,但是在使用过程中遇到了写入/读取乱码
(巨坑)问题,网上也找了相关的解决方法;本身对编码原理也不是很懂,因此较难,所以我附上我自己的解决方案;
方法1:
读取时使用ByteArrayOutputStream
进行输出,避免byte数组长度太短导致后面被截断而出现乱码。
//解决乱码的方法之一
byte[] bytes = new byte[3];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len;
while((len=raf.read(bytes))!=-1){
//截断后再次出现乱码
baos.write(bytes,0,len);
}
System.out.println(new String(baos.toByteArray()));
参考自:https://blog.csdn.net/mz4138/article/details/81396610
有关编码问题的可以参考:
https://blog.csdn.net/u013991521/article/details/47315787