23 Java学习之RandomAccessFile

Posted Hermioner

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了23 Java学习之RandomAccessFile相关的知识,希望对你有一定的参考价值。

一. 源码结构

 

      我们可以看到它的父类是Object,没有继承字节流、字符流家族中任何一个类。并且它实现了DataInput、DataOutput这两个接口,也就意味着这个类既可以读也可以写。RandomAccessFile 是随机访问文件(包括读/写)的类。它支持对文件随机访问的读取和写入,即我们可以从指定的位置读取/写入文件数据。

       需要注意的是,RandomAccessFile 虽然属于java.io包,但它不是InputStream或者OutputStream的子类;它也不同于FileInputStream和FileOutputStream。 FileInputStream 只能对文件进行读操作,而FileOutputStream 只能对文件进行写操作;但是,RandomAccessFile 同时支持文件的读和写,并且它支持随机访问。

       随机流不是IO流。

二. 存在意义

1、是JAVA I/O流体系中功能最丰富的文件内容访问类,它提供了众多方法来访问文件内容。

2、由于可以自由访问文件的任意位置,所以如果需要访问文件的部分内容,RandomAccessFile将是更好的选择。

3、可以用来访问保存数据记录的文件,文件的记录的大小不必相同,但是其大小和位置必须是可知的。

这个类在很多资料上翻译成中文都是:随机访问文件,在中文里,随机是具有不确定的含义,指一会访问这里,一会访问那里的意思。如果以这种语义来解释的话,就会感到很困惑。其实,Random在英文中不仅仅有随机,还有任意的意思。如果中文名为任意访问文件是不是就会更好的理解。任意表示我们可以指定文件中任何一个位置去操作一个文件。

4.实际项目举例:

   比如接到一个任务向一个已经有2G数据得txt文本里面末尾追加一行字,比如“我爱我家”。我们可能会直接将2G文件中得内容读取出来,然后转换成字符串,再用连接符将要新添加得这一行加上。可以,如果当这个数据文件变得越来越大,比如达到8G,可以我们得开发剩余内存只有5G。倘若我们还是采取老办法,强制读取数据并追加,则会产生内存溢出得异常现象。因此,就有了RandomAccessFile得产生。它可以进行任意访问,程序可以直接跳到任意地方来读写数据。我们不再需要将文件从头读到尾,只需要指定位置访问数据就行。

三. RandomAccessFile模式说明

RandomAccessFile共有4种模式:"r", "rw", "rws"和"rwd"。

"r"    以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。  
"rw"   打开以便读取和写入。
"rws"  打开以便读取和写入。相对于 "rw","rws" 还要求对“文件的内容”或“元数据”的每个更新都同步写入到基础存储设备。  
"rwd"  打开以便读取和写入,相对于 "rw","rwd" 还要求对“文件的内容”的每个更新都同步写入到基础存储设备。  

说明
1. 什么是“元数据”,即metadata?
英文解释如下:

The definition of metadata is "data about other data." With a file system, the data is contained in its files and directories, and the metadata tracks information about each of these objects: Is it a regular file, a directory, or a link? What is its size, creation date, last modified date, file owner, group owner, and access permissions?

大致意思是:
metadata是“关于数据的数据”。在文件系统中,数据被包含在文件和文件夹中;metadata信息包括:“数据是一个文件,一个目录还是一个链接”,“数据的创建时间(简称ctime)”,“最后一次修改时间(简称mtime)”,“数据拥有者”,“数据拥有群组”,“访问权限”等等。

2.  "rw", "rws", "rwd" 的区别。
当操作的文件是存储在本地的基础存储设备上时(如硬盘, NandFlash等),"rws" 或 "rwd", "rw" 才有区别。
当模式是 "rws" 并且 操作的是基础存储设备上的文件;那么,每次“更改文件内容[如write()写入数据]” 或 “修改文件元数据(如文件的mtime)”时,都会将这些改变同步到基础存储设备上。
当模式是 "rwd" 并且 操作的是基础存储设备上的文件;那么,每次“更改文件内容[如write()写入数据]”时,都会将这些改变同步到基础存储设备上。
当模式是 "rw" 并且 操作的是基础存储设备上的文件;那么,关闭文件时,会将“文件内容的修改”同步到基础存储设备上。至于,“更改文件内容”时,是否会立即同步,取决于系统底层实现。

 四. RandomAccessFile使用方法

下面来看下RandomAccessFile类中比较重要的2个方法,其他的和普通IO类似,在这里,就不详细说明了。

方法名 作用
getFilePointer() 返回文件记录指针的当前位置
seek(long pos) 将文件记录指针定位到pos的位置

1. 读取任意位置的数据
 1 package com.test.a;
 2 
 3 import java.io.IOException;
 4 import java.io.RandomAccessFile;
 5 
 6 public class Test {
 7     public static void main(String args[]) throws IOException{
 8         RandomAccessFile accessFile=new RandomAccessFile("C:\\\\Users\\\\hermioner\\\\Desktop\\\\test.txt", "r");
 9         System.out.println("RandomAccessFeile文件指针的初始位置:"+accessFile.getFilePointer());//1.
10         accessFile.seek(10);
11         System.out.println("RandomAccessFeile文件指针的初始位置:"+accessFile.getFilePointer());
12         byte buffer[]=new byte[40];
13         int hasRead=0;
14         while((hasRead=accessFile.read(buffer))>0) {
15             System.out.println(hasRead);
16             System.out.println("RandomAccessFeile文件指针的初始位置:"+accessFile.getFilePointer());
17             System.out.println(new String(buffer,0,hasRead));
18         }
19         
20     }
21 }
22 /**
23  * 1.第一处返回文件记录指针为0,即文本得最开始处。
24  * 2.当执行了seek(10)以后,就跳了10个字符,这是得文件记录指针应该在10
25  * 3.开始任意位置读取数据,一次读取hasRead个,最多读取40个。
26  * 4.第一次读取了40个,即此时得指针应该移动到了40+10=50处
27  * 5.第二次开始读取,只读取了34个(其中包含了一个换行符,因此实际上跳动了34个位置,即已经读取完了,此时应该在84位置处)。
28  */
29 
30 //RandomAccessFeile文件指针的初始位置:0
31 //RandomAccessFeile文件指针的初始位置:10
32 //40
33 //RandomAccessFeile文件指针的初始位置:50
34 //life,I love my family.
35 //I want to become
36 //34
37 //RandomAccessFeile文件指针的初始位置:84
38 // a professional software engineer.
View Code

note: test.txt文档中的内容如下:

2. seek

 1 package com.test.a;
 2 
 3 import java.io.IOException;
 4 import java.io.RandomAccessFile;
 5 
 6 public class Test {
 7     public static void main(String args[]) throws IOException{
 8         //以读写的方式创建了一个对象
 9         RandomAccessFile accessFile=new RandomAccessFile("C:\\\\Users\\\\hermioner\\\\Desktop\\\\test.txt", "rw");
10         accessFile.seek(2);
11         System.out.println(accessFile.getFilePointer());
12         accessFile.seek(2);
13         System.out.println(accessFile.getFilePointer());
14     }
15 }
View Code

说明:seek(number)可以用来每次定位到什么地方。当读取文件的时候,如果不去调用这个方法,getFilePointer的时候,会随着读取的位置变动而变动;倘若调用了seek,每次即使读取了数据位置有变动,一旦调用seek,就会回到seek指定的这个number处来,即getFilePointer等于这个Number.

3.  追加数据

 1 package com.test.a;
 2 
 3 import java.io.IOException;
 4 import java.io.RandomAccessFile;
 5 
 6 public class Test {
 7     public static void main(String args[]) throws IOException{
 8         //以读写的方式创建了一个对象
 9         RandomAccessFile accessFile=new RandomAccessFile("C:\\\\Users\\\\hermioner\\\\Desktop\\\\test.txt", "rw");
10         //将记录指针移动到文件的最后进行追加
11         accessFile.seek(accessFile.length());
12         accessFile.write("I can success".getBytes());
13     }
14 }
15 
16 
17 I love my life,I love my family.
18 I want to become a professional software engineer.I can success
View Code

4.  任意位置插入数据

 1 /** 
 2      * 实现向指定位置 
 3      * 插入数据 
 4      * @param fileName 文件名 
 5      * @param points 指针位置 
 6      * @param insertContent 插入内容 
 7      * **/  
 8     public static void insert(String fileName,long points,String insertContent){  
 9         try{  
10         File tmp=File.createTempFile("tmp", null);  
11         tmp.deleteOnExit();//在JVM退出时删除  
12           
13         RandomAccessFile raf=new RandomAccessFile(fileName, "rw");  
14         //创建一个临时文件夹来保存插入点后的数据  
15         FileOutputStream tmpOut=new FileOutputStream(tmp);  
16         FileInputStream tmpIn=new FileInputStream(tmp);  
17         raf.seek(points);  
18         /**将插入点后的内容读入临时文件夹**/  
19           
20         byte [] buff=new byte[1024];  
21         //用于保存临时读取的字节数  
22         int hasRead=0;  
23         //循环读取插入点后的内容  
24         while((hasRead=raf.read(buff))>0){  
25             // 将读取的数据写入临时文件中  
26             tmpOut.write(buff, 0, hasRead);  
27         }  
28           
29         //插入需要指定添加的数据  
30         raf.seek(points);//返回原来的插入处  
31         //追加需要追加的内容  
32         raf.write(insertContent.getBytes());  
33         //最后追加临时文件中的内容  
34         while((hasRead=tmpIn.read(buff))>0){  
35             raf.write(buff,0,hasRead);  
36         }  
37         }catch(Exception e){  
38             e.printStackTrace();  
39         }  
40     }  
View Code

说明:这里插入内容的原理就是:先把插入点后面的内容读入缓冲区,等插入完成后,再讲缓冲区(通过tempout写到了buffer中)的内容追加到文件的后面。

note:RandomAccessFile本身是没有这个任意位置插入数据的方法的。

 

 

文献参考:

https://www.cnblogs.com/zuochengsi-9/p/6485737.html

https://www.cnblogs.com/skywang12345/p/io_26.html

https://www.cnblogs.com/dongguacai/p/5699444.html

 

以上是关于23 Java学习之RandomAccessFile的主要内容,如果未能解决你的问题,请参考以下文章

Java进阶学习之设计原则(下)

java学习之jdbc的封装

《疯狂学习之Java设计模式之简单工厂模式》

java学习之单例模式(饿汉式与懒汉式)

23种设计模式学习之桥接模式

23种设计模式学习之享元模式