使用RandomAccessFile实现在文件中的增删改查
RandomAccessFile既可以读取文件内容,也可以向文件输出数据。同时,RandomAccessFile支持“随机访问”的方式,程序快可以直接跳转到文件的任意地方来读写数据。
由于RandomAccessFile可以自由访问文件的任意位置,所以如果需要访问文件的部分内容,而不是把文件从头读到尾,使用RandomAccessFile将是更好的选择。
与OutputStream、Writer等输出流不同的是,RandomAccessFile允许自由定义文件记录指针,RandomAccessFile可以不从开始的地方开始输出,因此RandomAccessFile可以向已存在的文件后追加内容。如果程序需要向已存在的文件后追加内容,则应该使用RandomAccessFile。
RandomAccessFile的方法虽然多,但它有一个最大的局限,就是只能读写文件,不能读写其他IO节点。
RandomAccessFile的一个重要使用场景就是网络请求中的多线程下载及断点续传。
//初始化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); } }