Java-RandomAccessFile使用

前几篇文章讲的是File以及相关流的使用,这节主要介绍随机访问文件的使用。

随机访问文件,如其名:使用它能够对文件进行一系列的随机操作。

1.RandomAccessFile基本介绍

看看它的类继承结构吧:

在这里插入图片描述
在这里插入图片描述

可以看出它继承了Object类以及实现了DataOutput, DataInput, Closeable三个接口,我们可以看出它并没有继承自IO流体系中的任何一个,但是它本身使用的是字节流

  • RandomAccessFile 的主要功能:对文件进行随机访问/读取/写入,可指定任意的写入/读取位置
  • RandomAccessFile 的特性:它属于字节流,可以读/写字节和所有原始类型的值;它具有文件指针,根据文件指针的位置进行读/写操作的。读写的同时,指针跟随移动。文件指针是我们·下一次读取或写入将开始的光标·。其值指示光标与文件开头的距离(以字节为单位)。

它的构造方法与IO体系非常的相似,第一个参数传入FilePath或者一个File即可,不同之处在于,它具有四个不同的模式,我们更具自己的需求实例化出相应模式的RandomAccessFile即可,模式如下表:(后两种不知道啥区别,知道的大佬们,给些链接。。。)

模式含义
“r”以只读的方式打开
“rw”以读/写的方式打开,如果文件不存在,则自动创建
“rws”该文件以读写模式打开。 对文件的内容及其元数据的任何修改立即被写入存储设备
“rwd”该文件以读写模式打开。 对文件内容的任何修改立即写入存储设备。

RandomAccessFile常用的方法:

  • seek(long n)–将文件指针设置在文件中的特定位置
  • skipBytes(long n)跳过n个字节
  • getFilePointer()– 获得文件指针
  • length()– 获取文件长度,我们可以通过使用其setLength()方法来扩展或截断文件。
  • writeUTF()–写入一个字符串
  • writeInt()/writeBoolean()/writeDouble()–写入原始类型的数据,不一一列出,详见API;相对应的一系列readXx()方法即读取;与IO体系中类似。

2.RandomAccessFile的使用

话不多说,直接上自己照着常用案例写的demo:

import java.io.*;
import java.net.URLEncoder;

//RandomAccessFile 本身是字节流
//seek()方法将文件指针设置在文件中的特定位置。
//skipBytes(long n)跳过n个字节
//getFilePointer() 获得文件指针
//length() 获取文件长度
//RandomAccessFile的length()方法返回文件的当前长度。我们可以通过使用其setLength()方法来扩展或截断文件。
//一系列的writeX()方法
//TODO 待解决  中文乱码


//四种模式
//"r" :只读模式
//"rw" : 读写模式,文件不存在时,会自动创建
//"rws" :读写模式,对文件的内容以及其元数据的任何修改立即被写入存储设备
//"rwd" : 读写模式,对文件内容的任何修改立即被写入存储设备

public class RandomAccessFileTest {
    static final String filePath = "G:" + File.separator + "io_test" + File.separator + "io_test.txt";
    static int count = 0;
    static String str = "random_access_file/测试";
    public static void main(String[] args) {
        //System.out.println(readOnlyFile());
        //wrMode();
        try {
            RandomAccessFile raf = new RandomAccessFile("G:"+File.separator+"random_access_file.txt","rw");
            //写入一个int类型的数据
           // raf.writeInt(count);
            //raf.writeDouble(5.6);
           // raf.write("你好?aa".getBytes());
            //raf.writeUTF(str);
            //int value = raf.readInt();
            //System.out.println(value);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //只读模式下的读取
    public static String readOnlyFile() {
        //
        RandomAccessFile raFileReadOnly = null;
        StringBuilder builder = new StringBuilder();
        try {
            //创建一个 只读的随机访问
            raFileReadOnly = new RandomAccessFile(filePath,
                    "r");
            byte[] bytes = new byte[1024];
            int len;
            while ((len = raFileReadOnly.read(bytes)) != -1) {
                builder.append(new String(bytes, 0, len, "gbk"));
            }
            //只读模式下只能读取,写入报错 拒绝访问
            // raFileReadOnly.write('c');
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (raFileReadOnly != null)
                    raFileReadOnly.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return builder.toString();
    }

    //读写模式
    public static void wrMode() {
        RandomAccessFile raFileRw = null;
        //StringBuilder builder = new StringBuilder();
        try {
            raFileRw = new RandomAccessFile(filePath, "rw");
            long len = raFileRw.length();
            raFileRw.skipBytes((int) len);
            raFileRw.writeUTF("测试一");
            long point = raFileRw.getFilePointer();
            System.out.println("point:" + point + "len:" + len);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (raFileRw != null) {
                try {
                    raFileRw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

无在文件的任意位置插入的实现:实现原理,将文件指针指向要插入的位置,将指针之后的内容存在缓冲,插入后追加到原文件。

 //pos 代表要插入的光标位置
    //str 要插入的值
    public static void insert(long pos, String str) {
        FileOutputStream fos = null;
        FileInputStream fis = null;
        try {
            RandomAccessFile raf
                    = new RandomAccessFile("G:" + File.separator + "random_access_file.txt",
                    "rw");
            //创建临时空文件
            File tempFile = File.createTempFile("temp", null);
            //在虚拟机终止时,请求删除此抽象路径名表示的文件或目录
            tempFile.deleteOnExit();
            //将光标移至要插入数据的位置
            raf.seek(pos);
            byte[] bytes = new byte[1024];
            int len;
            fos = new FileOutputStream(tempFile);
            fis = new FileInputStream(tempFile);
            //将光标后的数据写入临时文件
            while ((len = raf.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
            }
            fos.flush();

            //将光标移至要插入数据的位置,因为上面将插入位置之后的数据全部写入临时文件,光标为文件长度,需要重新定位
            raf.seek(pos);
            //插入内容
            raf.writeUTF(str);
            //再次追加原先的内容
            byte b[] = new byte[1024];
            int len1;
            while ((len1 = fis.read(b)) != -1) {
                raf.write(b, 0, len1);
            }

            raf.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close(fos);
            close(fis);

        }
    }

注意:包含基本API的使用,但是在使用过程中遇到了写入/读取乱码(巨坑)问题,网上也找了相关的解决方法;本身对编码原理也不是很懂,因此较难,所以我附上我自己的解决方案;

方法1:

读取时使用ByteArrayOutputStream进行输出,避免byte数组长度太短导致后面被截断而出现乱码。

    //解决乱码的方法之一
            byte[] bytes = new byte[3];
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int len;
            while((len=raf.read(bytes))!=-1){
                //截断后再次出现乱码
                baos.write(bytes,0,len);

            }
            System.out.println(new String(baos.toByteArray()));

参考自:https://blog.csdn.net/mz4138/article/details/81396610
有关编码问题的可以参考:
https://blog.csdn.net/u013991521/article/details/47315787

这篇文章比较细致:https://www.cnblogs.com/dongguacai/p/5699444.html

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用MappedByteBuffer可以将文件直接映射到内存中,通过内存操作来读写文件,从而提高性能。下面是使用MappedByteBuffer的示例代码: ```java try { // 创建 RandomAccessFile 对象 RandomAccessFile file = new RandomAccessFile("path/to/file.txt", "rw"); // 获取文件通道 FileChannel channel = file.getChannel(); // 将文件映射到内存中 MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, channel.size()); // 读取数据 byte[] data = new byte[buffer.limit()]; buffer.get(data); // 写入数据 String content = "Hello, World!"; buffer.put(content.getBytes()); // 刷新缓冲区内容到磁盘 buffer.force(); // 关闭资源 channel.close(); file.close(); } catch (IOException e) { e.printStackTrace(); } ``` 在上述代码中,首先通过RandomAccessFile对象获取文件通道,然后通过`map()`方法将文件映射到内存中的MappedByteBuffer对象。通过该对象可以直接对文件进行读写操作。 读取数据时,可以通过`get()`方法从MappedByteBuffer中获取字节数据。写入数据时,可以通过`put()`方法将字节数据写入MappedByteBuffer。 需要注意的是,在进行写入操作后,最好调用`force()`方法刷新缓冲区内容到磁盘,以确保数据被持久化保存。 最后,记得关闭资源,释放系统资源。 使用MappedByteBuffer可以减少磁盘I/O次数,提高读写性能,但需要注意内存映射文件的大小限制,过大的文件可能会导致内存溢出。此外,MappedByteBuffer适用于较大的文件读写,对于小文件可能带来较小的性能提升。因此,应根据具体情况评估是否使用MappedByteBuffer来优化RandomAccessFile的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值