Java中RandomAccessFile用法

掘金 · · 348 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

文章目录

介绍

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 文件会报错
在这里插入图片描述
D 盘下新建 abc 文件夹后再次运行程序,会在 d:/abc 下生成一个 f2 文件
在这里插入图片描述
上边介绍中说过,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();//释放系统资源
复制代码

输出结果
在这里插入图片描述

栗子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();//释放系统资源
复制代码

输出结果
在这里插入图片描述
操作记录指针方法

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 文件来完成加密和解密,加密之前内容
在这里插入图片描述
运行时如果输入错误的文件路径会进行提示
在这里插入图片描述
输入正确的文件路径时:
在这里插入图片描述
加密后的文件内容为
在这里插入图片描述
再次执行还原为原来的内容
在这里插入图片描述
栗子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 文件,内容使用十六进制查看:
在这里插入图片描述
来分析下程序:

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();
复制代码

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

本文来自:掘金

感谢作者:掘金

查看原文:Java中RandomAccessFile用法

348 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传