Java IO体系之RandomAccessFile浅析

时间:2023-03-08 16:23:17

Java IO体系之RandomAccessFile浅析

一、RandomAccessFile综述:

1.1RandomAccessFile简介

  • RandomAccessFile是java Io体系中功能最丰富的文件内容访问类。即可以读取文件内容,也可以向文件中写入内容。

  • RandomAccessFile的唯一父类是Object,与其他流父类不同。是用来访问那些保存数据记录的文件的,这样你就可以用seek( )方法来访问记录,并进行读写了。这些记录的大小不必相同;但是其大小和位置必须是可知的。

  • 由于RandomAccessFile可以*访问文件的任意位置,所以如果我们希望只访问文件的部分内容,RandomAccessFile将是更好的选择。

  • 与OutputStearm,Writer等输出流不同的是,RandomAccessFile类允许*定位文件记录指针,程序可以直接跳到文件的任意位置来读写数据,所以RandomAccessFile可以不从文件开始的地方进行输出,如果需要向已存在的文件后追加内容。则可以使用RandomAccessFile。

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

1.2、RandomAccessFile数据结构

Java IO体系之RandomAccessFile浅析

  这个是JDK上的截图,我们可以看到它的父类是Object,没有继承字节流、字符流家族中任何一个类。实际上,除了实现DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口,意味着这个类即可以读也可以写),它和字节流、字符流类系毫不相干,甚至都没有用InputStream和OutputStream已经准备好的功能;它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是从零开始写的。这可能是因为RandomAccessFile能在文件里面前后移动,所以它的行为与其它的I/O类有些根本性的不同。总而言之,它是一个直接继承Object的,独立的类。

1.3.RandomAccessFile的整体介绍

  • RandomAccessFile类包含了一个记录指针,用以标识当前读写处的位置,当程序新创建一个RandomAccessFile对象时,该对象的文件记录指针位于文件头(也就是0处),当读/写了n个字节后,文件记录指针将会向后移动n个字节。除此之外,RandomAccessFile可以*的移动记录指针,即可以向前移动,也可以向后移动。RandomAccessFile包含了以下两个方法来操作文件的记录指针:RandomAccessFile即可以读文件,也可以写,所以它即包含了完全类似于InputStream的3个read()方法,其用法和InputStream的3个read()方法完全一样;也包含了完全类似于OutputStream的3个write()方法,其用法和OutputStream的3个Writer()方法完全一样。除此之外,RandomAccessFile还包含了一系类的readXXX()和writeXXX()方法来完成输入和输出。

    • long getFilePointer(); 返回文件记录指针的当前位置
    • void seek(long pos); 将文件记录指针定位到pos位置
  • RandomAccessFile有两个构造器,其实这两个构造器基本相同,只是指定文件的形式不同而已,一个使用String参数来指定文件名,一个使用File参数来指定文件本身。除此之外,创建RandomAccessFile对象还需要指定一个mode参数。该参数指定RandomAccessFile的访问模式,有以下4个值:

    • “r” 以只读方式来打开指定文件夹。如果试图对该RandomAccessFile执行写入方法,都将抛出IOException异常。

    • “rw” 以读,写方式打开指定文件。如果该文件尚不存在,则试图创建该文件。

    • “rws” 以读,写方式打开指定文件。相对于”rw” 模式,还要求对文件内容或元数据的每个更新都同步写入到底层设备。

    • “rwd” 以读,写方式打开指定文件。相对于”rw” 模式,还要求对文件内容每个更新都同步写入到底层设备。

1.4工作方式

  • 基本上,RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream粘起来,再加上它自己的一些方法,比如定位用的getFilePointer( ),在文件里移动用的seek( ),以及判断文件大小的length( )。此外,它的构造函数还要一个表示以只读方式("r"),还是以读写方式("rw")打开文件的参数 (和C的fopen( )一模一样)。它不支持只写文件,从这一点上看,假如RandomAccessFile继承了DataInputStream,它也许会干得更好。
  • 只有RandomAccessFile才有seek方法,而这个方法也只适用于文件。BufferedInputStream有一个mark( )方法,你可以用它来设定标记(把结果保存在一个内部变量里),然后再调用reset( )返回这个位置,但是它的功能太弱了,而且也不怎么实用。
  • RandomAccessFile的绝大多数功能,如果不是全部的话,已经被JDK1.4的nio的"内存映射文件(memory-mapped files)"给取代了。

二、RandomAccessFile方法摘要:

构造方法摘要
RandomAccessFile(File file, String mode)
          创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。
RandomAccessFile(String name, String mode)

          创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。
方法摘要
 void close()

          关闭此随机访问文件流并释放与该流关联的所有系统资源。
 FileChannel getChannel()

          返回与此文件关联的唯一 FileChannel
对象。
 FileDescriptor getFD()

          返回与此流关联的不透明文件描述符对象。
 long getFilePointer()

          返回此文件中的当前偏移量。
 long length()

          返回此文件的长度。
 int read()

          从此文件中读取一个数据字节。
 int read(byte[] b)

          将最多 b.length 个数据字节从此文件读入 byte 数组。
 int read(byte[] b,
int off, int len)

          将最多 len 个数据字节从此文件读入 byte
数组。
 boolean readBoolean()

          从此文件读取一个 boolean
 byte readByte()

          从此文件读取一个有符号的八位值。
 char readChar()

          从此文件读取一个字符。
 double readDouble()

          从此文件读取一个 double
 float readFloat()

          从此文件读取一个 float
 void readFully(byte[] b)

          将 b.length 个字节从此文件读入 byte 数组,并从当前文件指针开始。
 void readFully(byte[] b,
int off, int len)

          将正好 len 个字节从此文件读入 byte
数组,并从当前文件指针开始。
 int readInt()

          从此文件读取一个有符号的 32 位整数。
 String readLine()

          从此文件读取文本的下一行。
 long readLong()

          从此文件读取一个有符号的 64 位整数。
 short readShort()

          从此文件读取一个有符号的 16 位数。
 int readUnsignedByte()

          从此文件读取一个无符号的八位数。
 int readUnsignedShort()

          从此文件读取一个无符号的 16 位数。
 String readUTF()

          从此文件读取一个字符串。
 void seek(long pos)

          设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。
 void setLength(long newLength)

          设置此文件的长度。
 int skipBytes(int n)

          尝试跳过输入的 n 个字节以丢弃跳过的字节。
 void write(byte[] b)

          将 b.length 个字节从指定 byte 数组写入到此文件,并从当前文件指针开始。
 void write(byte[] b,
int off, int len)

          将 len 个字节从指定 byte
数组写入到此文件,并从偏移量 off 处开始。
 void write(int b)

          向此文件写入指定的字节。
 void writeBoolean(boolean v)

          按单字节值将 boolean 写入该文件。
 void writeByte(int v)

          按单字节值将 byte 写入该文件。
 void writeBytes(String s)

          按字节序列将该字符串写入该文件。
 void writeChar(int v)

          按双字节值将 char 写入该文件,先写高字节。
 void writeChars(String s)

          按字符序列将一个字符串写入该文件。
 void writeDouble(double v)

          使用 Double 类中的 doubleToLongBits
方法将双精度参数转换为一个 long,然后按八字节数量将该 long
值写入该文件,先定高字节。
 void writeFloat(float v)

          使用 Float 类中的 floatToIntBits 方法将浮点参数转换为一个
int,然后按四字节数量将该 int 值写入该文件,先写高字节。
 void writeInt(int v)

          按四个字节将 int 写入该文件,先写高字节。
 void writeLong(long v)

          按八个字节将 long 写入该文件,先写高字节。
 void writeShort(int v)

          按两个字节将 short 写入该文件,先写高字节。
 void writeUTF(String str)

          使用 modified UTF-8
编码以与机器无关的方式将一个字符串写入该文件。

三、实例分析

 1 package me.io.raffile;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.RandomAccessFile;
6 import java.util.Arrays;
7
8 public class RafDemo {
9
10 public static void main(String[] args) throws IOException {
11 File demo = new File("demo");
12 if(!demo.exists())
13 demo.mkdir();
14 File file = new File(demo, "raf.dat");
15 if (!file.exists())
16 file.createNewFile();
17 RandomAccessFile raf = new RandomAccessFile(file, "rw");
18 //指针的位置
19 System.out.println(raf.getFilePointer());
20 raf.write('A');//只写了一个字节
21 System.out.println(raf.getFilePointer());
22
23 raf.write('B');
24 System.out.println(raf.getFilePointer());
25
26 //Java中最大的整数
27 int i = 0x7fffffff;
28
29 //占四个字节,如果要把i写进去,就得写四次
30 raf.write(i >>> 24);//高八位
31 raf.write(i >>> 16);
32 raf.write(i >>> 8);
33 raf.write(i);
34
35 //可以直接写一个int
36 raf.writeInt(i);
37
38 String string = "中";
39 byte[] bytes = string.getBytes();
40 raf.write(bytes);
41 System.out.println(raf.length());
42
43 //读文件,必须把指针移到头部
44 raf.seek(0);
45 //一次性读取,把文件中的内容都堵到字节数组
46 byte[] buf = new byte[(int) raf.length()];
47 raf.read(buf);
48 System.out.println(Arrays.toString(buf));
49 for (byte b : buf) {
50 System.out.print(Integer.toHexString(b & 0xff) + " ");
51 }
52 raf.close();
53 }
54 }

三、专题总结(FIle与RandomAccessFile)

注:这里File类的介绍可参见我的上一篇博文

java.io.File类用于表示文件(目录)
File类只用于表示文件(目录)的信息(名称、大小等),不能用于文件内容的访问

RandomAccessFile java提供的对文件内容的访问,既可以读文件,也可以写文件。
RandomAccessFile支持随机访问文件,可以访问文件的任意位置

(1)java文件模型
  在硬盘上的文件是byte byte byte存储的,是数据的集合
(2)打开文件
  有两种模式"rw"(读写)  "r"(只读)
  RandomAccessFile raf = new RandomeAccessFile(file,"rw")
  文件指针,打开文件时指针在开头 pointer = 0;
(3) 写方法
    raf.write(int)--->只写一个字节(后8位),同时指针指向下一个位置,准备再次写入
(4)读方法
   int b = raf.read()--->读一个字节

(5)文件读写完成以后一定要关闭(Oracle官方说明)

参考文章:

  http://blog.****.net/nightcurtis/article/details/51384126

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