RandomAccessFile是什么?
RandomAccessFile既可以读取文件内容,也可以向文件输出数据。同时,RandomAccessFile支持“随机访问”的方式,程序快可以直接跳转到文件的任意地方来读写数据。
由于RandomAccessFile可以*访问文件的任意位置,所以如果需要访问文件的部分内容,而不是把文件从头读到尾,使用RandomAccessFile将是更好的选择。
与OutputStream、Writer等输出流不同的是,RandomAccessFile允许*定义文件记录指针,RandomAccessFile可以不从开始的地方开始输出,因此RandomAccessFile可以向已存在的文件后追加内容。如果程序需要向已存在的文件后追加内容,则应该使用RandomAccessFile。
RandomAccessFile的方法虽然多,但它有一个最大的局限,就是只能读写文件,不能读写其他IO节点。
RandomAccessFile的一个重要使用场景就是网络请求中的多线程下载及断点续传。
RandomAccessFile讲解
读写文件模式
模式 | 解释 |
---|---|
r | 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。 |
rw | 打开以便读取和写入。 |
rws | 打开以便读取和写入。相对于 "rw","rws" 还要求对“文件的内容”或“元数据”的每个更新都同步写入到基础存储设备。 |
rwd | 打开以便读取和写入,相对于 "rw","rwd" 还要求对“文件的内容”的每个更新都同步写入到基础存储设备。 |
常用方法
//初始化RandomAccessFile
RandomAccessFile raf=new RandomAccessFile(path, "r");
//获取RandomAccessFile对象文件指针的位置,初始位置是0
System.out.println("RandomAccessFile文件指针的初始位置:"+raf.getFilePointer());
raf.seek(pointe);//移动文件指针位置
byte[] buff=new byte[1024];
//用于保存实际读取的字节数
int hasRead=0;
//循环读取
while((hasRead=raf.read(buff))>0){
//打印读取的内容,并将字节转为字符串输入
System.out.println(new String(buff,0,hasRead));
}
案例:使用RandomAccessFile进行对象的增删改操作
构建工具类
将操作RandomAccessFile的四种读取方式整合并确定目标文件
public class RAFTestFactory{
private static final String url = "test.txt";
private static final String [] model = {"r","rw","rws","rwd"};
public static RandomAccessFile getRAFWithModelR() throws FileNotFoundException {
RandomAccessFile raf = new RandomAccessFile(new File(url), model[0]);
return raf;
}
public static RandomAccessFile getRAFWithModelRW() throws FileNotFoundException {
RandomAccessFile raf = new RandomAccessFile(new File(url), model[1]);
return raf;
}
public static RandomAccessFile getRAFWithModelRWS() throws FileNotFoundException {
RandomAccessFile raf = new RandomAccessFile(new File(url), model[2]);
return raf;
}
public static RandomAccessFile getRAFWithModelRWD() throws FileNotFoundException {
RandomAccessFile raf = new RandomAccessFile(new File(url), model[3]);
return raf;
}
}
创建实体类,需要实现Serializable接口
public class Project implements Serializable {
private static final long serialVersionUID = 1L;
private int id; //4个字节
private String name;
private double price; //8个字节
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Project() {
}
public Project(int id, String name, double price) {
super();
StringBuilder builder=null;
if(name!=null){
builder=new StringBuilder(name);
}else{
builder=new StringBuilder(15);//源码中显示默认是16个字符容量,我们指定为15个字符容量(30个字节)
}
builder.setLength(15);//固定长度为15个
this.id = id;
this.name = builder.toString();
this.price = price;
}
//返回每个对象所占内存大小
public static int size(){
return 4+8+30;
}
@Override
public String toString() {
return "Project{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
'}';
}
}
整合操作对象的方法,使其能进行在文件中增删改功能
public class OperationObject {
//使用该方法不断添加
public void writeProject01(Project project) throws IOException {
RandomAccessFile raf = RAFTestFactory.getRAFWithModelRW();
byte[] header = new byte[100];
raf.write(header, 0, header.length);
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(project);
header = bo.toByteArray();
//将指针移到末尾
raf.seek(raf.length());
raf.write(header,0,header.length);
raf.close();
System.out.println("writeProject01写入成功");
}
public void writeProject02(Project project) throws IOException {
RandomAccessFile raf = RAFTestFactory.getRAFWithModelRW();
//指针移到末尾
raf.seek(raf.length());
//将id以int形式写入(4byte)
raf.writeInt(project.getId());
raf.writeChars(project.getName());
raf.writeDouble(project.getPrice());
raf.close();
System.out.println("writeProject02写入成功");
}
//输入:要读取的id值
public Project readProject01(int index) throws IOException {
RandomAccessFile raf = RAFTestFactory.getRAFWithModelRW();
//使用seek方法来读取对象存放的位置
raf.seek((index-1)*Project.size());
Project project=new Project();
//设置project的值
project.setId(raf.readInt());
project.setName(readName(raf));
project.setPrice(raf.readDouble());
raf.close();
System.out.println("readProject01读出成功");
System.out.println(project);
return project;
}
//输入:要删除的id值
public void deleteProject01(int index) throws IOException {
RandomAccessFile raf = RAFTestFactory.getRAFWithModelRW();
byte[] bytes = new byte[1024];
//将后面的project拷贝到bytes中
raf.seek((index)*Project.size());
int hasRead = 0;
String s = null;
while((hasRead=raf.read(bytes))>0){
s = new String(bytes,0,hasRead);
}
System.out.println(s);
//字符串变byte
bytes = s.getBytes();
//将光标移动到要删除的对象前
raf.seek((index-1)*Project.size());
//用后面的字符将该对象覆盖
raf.write(bytes);
long m = raf.length();
//将多出来的字符清除
for(int i = 0;i<m;i++){
System.out.println("进入循环");
raf.writeInt(0);
}
raf.close();
System.out.println("Delete "+index);
}
//更新思路:先删除。,在添加
public void UpdateProject(int i,Project project) throws IOException {
OperationObject operationObject = new OperationObject();
Project project1 = operationObject.readProject01(i);
project1.setId(i);
project1.setName(project.getName());
project1.setPrice(project.getPrice());
operationObject.writeProject02(project1);
System.out.println("success");
}
//获得文件的大小
public long getSize() throws FileNotFoundException {
File file = new File("test.txt");
if (file.exists() && file.isFile()){
return file.length();
}
return 0;
}
private static String readName(RandomAccessFile randomAccessFile) throws IOException{
char[] name=new char[15];
//Character 类用于对单个字符进行操作。
Character character=null;
int index=0;
for(int i=0;i<name.length;i++){
//将字符读到name中
name[i]=randomAccessFile.readChar();
character=new Character(name[i]);
//判断character是否结束
if(!character.equals('\u0000')){
index++;
}
}
//将结束符替换为空格,返回字符
return (new String(name)).replace('\u0000',' ').substring(0, index);
}
}