likes
comments
collection
share

Java中RandomAccessFile用法

作者站长头像
站长
· 阅读数 4

文章目录

介绍

java.io.RandomAccessFile 1、读写文件的工具 2、将文件中的字节数据,当作数组,用下标访问指定位置的字节值

RandomAccessFile 既可以读取文件内容,也可以向文件输出数据。同时,RandomAccessFile 支持“随机访问”的方式,程序快可以直接跳转到文件的任意地方来读写数据。

由于 RandomAccessFile 可以自由访问文件的任意位置,所以如果需要访问文件的部分内容,而不是把文件从头读到尾,使用 RandomAccessFile 将是更好的选择。

RandomAccessFile 允许自由定义文件记录指针,RandomAccessFile 可以不从开始的地方开始输出,因此 RandomAccessFile 可以向已存在的文件后追加内容。如果程序需要向已存在的文件后追加内容,则应该使用 RandomAccessFile。

RandomAccessFile 的方法虽然多,但它有一个最大的局限,就是只能读写文件,不能读写其他 IO 节点。

RandomAccessFile 的一个重要使用场景就是网络请求中的多线程下载及断点续传。

关于字节 计算机中的所有数据都是由 0、1 组成,每 8 位为 1 个字节,读写数据的时候都是按字节读写的。

每 4 位可以由一个十六进制字符表示,如 10010110 00010101 11111001 96             15             f9

所以每两个十六进制字符就可以表示 1 个字节。

abc 这三个字母在计算机中的存储是这样的: a 在计算机中存储的十进制值是 97,二进制为 01100001, 十六进制为 61 b 十进制 98,二进制为 01100010,十六进制为 62 c 十进制 99,二进制为 01100011,十六进制为 63

RandomAccessFile 使用

创建对象

RandomAccessFile raf = new RandomAccessFile(文件,r);
RandomAccessFile raf = new RandomAccessFile(文件,rw);

这个文件可以是字符串d:/abc,也可以是封装好的File对象d:/abc

r 为只读,rw 为读写。读文件是输入,读入内存。写文件是输出,都是相对内存来说的。

写方法

write(int b)

int 四个字节中,只输出末尾的一个字节值:[1][2][3][4]->[4] 适用于单字节范围内的值,切掉前边3个不会有数据损失

write(byte[] buff)

输出数组中全部字节值

write(byte[] buff,int from,int length)

输出数组中从 from 开始的 length 个

栗子1:写入练习

public class Main {
    public static void main(String[] args) throws Exception {
        /*
         * 1、如果文件不存在,新建文件
         * 2、如果目录不存在,出现异常
         */
        RandomAccessFile raf = new RandomAccessFile("d:/abc/f2", "rw");
        raf.write(97);
        raf.write(98);
        raf.write(99);
        raf.write(356);

        byte[] buff = {
                101, 102, 103, 104, 105,
                106, 107, 108, 109, 110
        };
        raf.write(buff);
        raf.write(buff, 6, 3);
        raf.close();//释放系统资源
    }
}

运行结果: 如果 D 盘下没有 abc 文件会报错 Java中RandomAccessFile用法 D 盘下新建 abc 文件夹后再次运行程序,会在 d:/abc 下生成一个 f2 文件 Java中RandomAccessFile用法 上边介绍中说过,a 在计算机中存储的值是 97(十进制),二进制为 01100001,每四字节可用一个十六进制表示,所以十六进制为 61。

97、98、99 分别转为十六进制为 61、62、63

356 二进制为 0001 0110 0100,十六进制为 164,补全 8 位为 00 00 01 64,由于write(int b) 方法每四字节只输出最后一个字节,所以只输出了 64。

只要不超过 255 就不会有数据损失,97 十六进制 00 00 00 61,只输出最后 61,没有数据损失。

然后输出数组 buff,raf.write(buff)方法可以把数组全部输出,数组中 10 个数字都转为十六进制: 101–>65 … 110–>6e

raf.write(buff,6,3)从数组下标 6 开始,输出 3 个,也就是 107、108、109。分别转为十六进制: 107–>6b 108–>6c 109–>6d

raf.close()方法通知虚拟机把底层资源释放掉,虚拟机再通知操作系统把系统资源释放掉,之后不能再向文件输出数据了。

读取方法

read();

读取一个字节值,前补 3 个0,转成 int,如

10001010 11000011 11110111 01110001 10001010 将被读取出来,然后前边补 3 个 0,也就是: 00000000 00000000 00000000 10001010

读取结束再读取会返回 -1。

read(byte[] buff);

根据数组容量,读取一批字节值放入数组。返回这一批的字节数量,字节数量不一定是数组容量。 如果读取结束,返回-1

栗子2:单字节读取

我们修改上面的代码,在raf.close();这行之前增加以下代码

		//读取
        raf.seek(0);//定位下标
        //单字节读取标准格式
        int b;
        while ((b = raf.read()) != -1) {
            System.out.println(b);
        }
        //随着读取,下标也会向后移动
        raf.seek(0);
        raf.close();//释放系统资源

输出结果 Java中RandomAccessFile用法

栗子3:批量读取

我们还是修改栗子1,在raf.close();这行之前增加以下代码

		//随着读取,下标也会向后移动,已经移动到末尾
        raf.seek(0);
        //批量读取标准格式
        buff = new byte[5];
        int n;//保存每批数量
        while ((n = raf.read(buff)) != -1) {
            System.out.println(
                    n + "-" + Arrays.toString(buff));
        }
        raf.close();//释放系统资源

输出结果 Java中RandomAccessFile用法 操作记录指针方法

void seek(long pos)

将文件指针定位到 pos 位置,然后下次读文件数据的时候从该位置读取。

getFilePointer()

获得下标当前位置

当程序新创建一个 RandomAccessFile 对象时,该对象的文件指针记录位于文件头(也就是0处),当读/写了 n 个字节后,文件记录指针将会后移 n 个字节。除此之外,RandomAccessFile 还可以自由移动该记录指针。

栗子4:文件加密解密,单字节实现

public class Main {
    public static void main(String[] args) {
        System.out.println("请输入文件路径");
        String s = new Scanner(System.in).nextLine();
        File f = new File(s);
        if (!(f.isFile())) {
            System.out.println("请输入正确的文件路径");
            return;
        }
        System.out.println("KEY:");
        int key = new Scanner(System.in).nextInt();
        try {
            encript(f, key);
            System.out.println("加密/解密完成");
        } catch (Exception e) {
            System.out.println("加密/解密失败");
            e.printStackTrace();
        }
    }

    private static void encript(File f, int key) throws Exception {
        /*
         *1、新建 RandomAccessFile 赋值给 raf
         *2、单字节循环读取,读取的字节值赋值给 b
         *	3、b异或 k,结果再赋给 b
         *	4、定位下标到 raf.getFilePointer()-1
         *	5、将字节值 b写回到文件
         *6、关闭 raf
         */
        RandomAccessFile raf = new RandomAccessFile(f, "rw");
        int b;
        while ((b = raf.read()) != -1) {
            b ^= key;
            raf.seek(raf.getFilePointer() - 1);
            raf.write(b);
        }
        raf.close();
    }
}

我们用 d:/abc/f2 文件来完成加密和解密,加密之前内容 Java中RandomAccessFile用法 运行时如果输入错误的文件路径会进行提示 Java中RandomAccessFile用法 输入正确的文件路径时: Java中RandomAccessFile用法 加密后的文件内容为 Java中RandomAccessFile用法 再次执行还原为原来的内容 Java中RandomAccessFile用法 栗子5:文件加密解密,多字节实现

单字节实现效率低,我们改用多字节实现,修改加密解密方法:

private static void encript(File f, int key) throws Exception {
        RandomAccessFile raf = new RandomAccessFile(f, "rw");
        byte[] buff = new byte[8192];
        int n;//保存每一批的数量
        while ((n = raf.read(buff)) != -1) {
            //前边都是8192,最后一批可能不是8192
            for (int i = 0; i < n; i++) {
                buff[i] ^= key;
            }
            raf.seek(raf.getFilePointer() - n);
            //不用全部写回,因为最后一批可能只有一部分
            raf.write(buff, 0, n);
        }
    }

其他方法

writeInt(int i)

输出 int 的4个字节值,完整输出

writeDouble(double d)

输出 double 的 8 个字节值,完整输出… writeFloat 等都有对应的方法。

writeUTF(String s)

先输出 2 个字节,表示字符串的字节长度,再输出相应字符串的字节值,例如 “abc” 会这样输出 00 03 61 62 63

readInt()

读 4 个字节转成 int

readDouble()

读 8 个字节转成 double …

readUTF()

先读取两个字节,来确定字符串的字节长度,再读取字节值转成 String 如果读取结束再读取,出现 EOFException(EOF-End of file)

栗子6:其他方法练习

public static void main(String[] args) throws Exception {//选择父类型
        RandomAccessFile raf = new RandomAccessFile("d:/abc/f3", "rw");
        raf.write(97);
        raf.write(98);
        raf.write(99);
        raf.writeInt(97);
        raf.writeInt(98);
        raf.writeInt(99);
        raf.writeShort(100);
        raf.writeBoolean(false);
        raf.writeChar('中');
        raf.writeUTF("abc中文");
        raf.writeDouble(3.14);
        raf.close();
    }

运行结程序会在 D 盘 abc 文件夹下生成 f3 文件,内容使用十六进制查看: Java中RandomAccessFile用法 来分析下程序:

raf.write(97);
raf.write(98);
raf.write(99);

输出末尾字节

61 62 63

raf.writeInt(97);
raf.writeInt(98);
raf.writeInt(99);

输出完整 4 个字节

00 00 00 61 
00 00 00 62 
00 00 00 63 
raf.writeShort(100);

short 两个字节,所以输出

00 64

raf.writeBoolean(false);

布尔值只有 1 个字节,false 是 0

所以输出

00

raf.writeChar('中');

char 两个字节,所以输出

4E 2D

raf.writeUTF("abc中文");

00 09 9个字节 61 62 63 E4 B8 AD E6 96 87

raf.writeDouble(3.14);

double 8个字节

40 09 1E B8 51 EB 85 1F

栗子7:文件读取

修改上面的代码,在 raf.close(); 之前增加以下代码:

		//文件读取
        raf.seek(0);
        System.out.println(raf.read());
        System.out.println(raf.read());
        System.out.println(raf.read());
        System.out.println(raf.readInt());
        System.out.println(raf.readInt());
        System.out.println(raf.readInt());
        System.out.println(raf.readShort());
        System.out.println(raf.readBoolean());
        System.out.println(raf.readChar());
        System.out.println(raf.readUTF());
        System.out.println(raf.readDouble());
        raf.close();

运行结果 Java中RandomAccessFile用法 最后需要注意,当 RandomAccessFile 向指定文件中插入内容时,将会覆盖掉原有内容。如果不想覆盖掉,则需要将原有内容先读取出来,然后先把插入内容插入后再把原有内容追加到插入内容后。

转载自:https://juejin.cn/post/7016534074114179109
评论
请登录