Java IO流——RandomAccessFile(随机读写)

Java IO流——RandomAccessFile(随机读写)

 

 

一、RandomAccessFile简介

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

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

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

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

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


二、RandomAccessFile中的方法

1.RandomAccessFile的构造函数

RandomAccessFile类有两个构造函数,其实这两个构造函数基本相同,只不过是指定文件的形式不同——一个需要使用String参数来指定文件名,一个使用File参数来指定文件本身。除此之外,创建RandomAccessFile对象时还需要指定一个mode参数,该参数指定RandomAccessFile的访问模式,一共有4种模式。

 

2.RandomAccessFile的重要方法

  RandomAccessFile  既可以读文件,也可以写文件,所以类似于InputStream的read()方法,以及类似于OutputStream的write()方法,RandomAccessFile都具备。除此之外,RandomAccessFile具备两个特有的方法,来支持其随机访问的特性。

RandomAccessFile对象包含了一个记录指针,用以标识当前读写处的位置,当程序新创建一个RandomAccessFile对象时,该对象的文件指针记录位于文件头(也就是0处),当读/写了n个字节后,文件记录指针将会后移n个字节。除此之外,RandomAccessFile还可以自由移动该记录指针。下面就是RandomAccessFile具有的两个特殊方法,来操作记录指针,实现随机访问:

long getFilePointer( ); // 返回文件记录指针的当前位置
void seek(long pos );  // 将文件指针定位到pos位置

其他方法:

// 指定文件的光标位置,通俗点说就是指定你的光标位置,然后下次读文件数据的时候从该位置读取。
1、seek()

// 我们注意到这是一个long类型的返回值,字面意思就是返回当前的文件光标位置。这样方便我们后面读取插入。
2、getFilePointer()

// 毫无疑问的方法,文件的长度,返回long类型。注意它并不会受光标的影响。只会反应客观的文本长度。
3、length()

// 这些方法跟readstream中的方法一样,例如最后一个:定义缓冲数组,从数组的off偏移量位置开始写,读取转换为数组数据达到len个字节。总之这是一个读文件内容的标准操作api。
4、read()、read(byte[] b)、read(byte[] b,int off,int len)

//  这些方法都是去read | write 每一个字符,个人感觉就是返回他们的ASCII码
当然如果专家们有异议可以指出,我测试的时候至少是这么感觉得。
大家也可以自己试一下。比如readLong就是要求你的文本内容必须有八个字符,不然会报错。
5、readDouble()、 readFloat()、readBoolean()、 readInt()
readLong()、 readShort()、 readByte()、 readChar()
writeDouble()、 writeFloat()、 writeBoolean()、 writeInt()
writeLong()、 writeShort() 、writeByte()、 writeChar()

// 这个方法的作用就是将文本中的内容填满这个缓冲区b。如果缓冲b不能被填满,那么读取流的过程将被阻塞,如果发现是流的结尾,那么会抛出异常。这个过程就比较像“凑齐一车人在发车,
不然不走”。
6、readFully(byte[] b): // 它返回的就是nio通信中的file的唯一channel, FileChannel 7、getChannel() // 跳过n字节的位置,相对于当前的point。 8、skipBytes(int n);

 

三、RandomAccessFile的使用

1:多线程写文件

 利用 RandomAccessFile 实现文件的多线程下载,即多线程下载一个文件时,将文件分成几块,每块用不同的线程进行下载。下面是一个利用多线程在写文件时的例子,其中预先分配文件所需要的空间,然后在所分配的空间中进行分块,然后写入:

/** 
 * 测试利用多线程进行文件的写操作 
 */  
public class Test {  
  
    public static void main(String[] args) throws Exception {  
        // 预分配文件所占的磁盘空间,磁盘中会创建一个指定大小的文件  
        RandomAccessFile raf = new RandomAccessFile("D://abc.txt", "rw");  
        raf.setLength(1024*1024); // 预分配 1M 的文件空间  
        raf.close();  
          
        // 所要写入的文件内容  
        String s1 = "第一个字符串";  
        String s2 = "第二个字符串";  
        String s3 = "第三个字符串";  
        String s4 = "第四个字符串";  
        String s5 = "第五个字符串";  
          
        // 利用多线程同时写入一个文件  
        new FileWriteThread(1024*1,s1.getBytes()).start(); // 从文件的1024字节之后开始写入数据  
        new FileWriteThread(1024*2,s2.getBytes()).start(); // 从文件的2048字节之后开始写入数据  
        new FileWriteThread(1024*3,s3.getBytes()).start(); // 从文件的3072字节之后开始写入数据  
        new FileWriteThread(1024*4,s4.getBytes()).start(); // 从文件的4096字节之后开始写入数据  
        new FileWriteThread(1024*5,s5.getBytes()).start(); // 从文件的5120字节之后开始写入数据  
    }  
      
    // 利用线程在文件的指定位置写入指定数据  
    static class FileWriteThread extends Thread{  
        private int skip;  
        private byte[] content;  
          
        public FileWriteThread(int skip,byte[] content){  
            this.skip = skip;  
            this.content = content;  
        }  
          
        public void run(){  
            RandomAccessFile raf = null;  
            try {  
                raf = new RandomAccessFile("D://abc.txt", "rw");  
                raf.seek(skip);  
                raf.write(content);  
            } catch (FileNotFoundException e) {  
                e.printStackTrace();  
            } catch (IOException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            } finally {  
                try {  
                    raf.close();  
                } catch (Exception e) {  
                }  
            }  
        }  
    }  
  
}  

 

2、使用RandomAccessFile实现从指定位置读取文件的功能

public  static void main(String[] args)throws IOException {
        String filePath="E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\Test.txt";
        RandomAccessFile raf=null;
        File file=null;
        try {
            file=new File(filePath);
            raf=new RandomAccessFile(file,"r");
            // 获取 RandomAccessFile对象文件指针的位置,初始位置为0
            System.out.print("输入内容:"+raf.getFilePointer());
            //移动文件记录指针的位置
            raf.seek(1000);

            byte[] b=new byte[1024];
            int hasRead=0;
            //循环读取文件
            while((hasRead=raf.read(b))>0){
                //输出文件读取的内容
                System.out.print(new String(b,0,hasRead));
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            raf.close();
        }
    }

3、使用RandomAccessFile实现向文件中追加内容的功能

public class RandomAccessFileTest2 {
    public  static void main(String[] args)throws IOException {
        String filePath="E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\Test.txt";
        RandomAccessFile raf=null;
        File file=null;
        try {
            file=new File(filePath);
            // 以读写的方式打开一个RandomAccessFile对象
            raf=new RandomAccessFile(file,"rw");
            //将记录指针移动到该文件的最后
            raf.seek(raf.length());
            //向文件末尾追加内容
            raf.writeChars("这是追加内容。。");
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            raf.close();
        }
    }

4、使用RandomAccessFile实现向文件指定位置插入内容的功能

注:RandomAccessFile  不能向文件的指定位置插入内容,如果直接将文件记录指针移动到中间某位置后开始输出,则新输出的内容会覆盖文件原有的内容,如果需要向指定位置插入内容,程序需要先把插入点后面的内容写入缓存区,等把需要插入的数据写入到文件后,再将缓存区的内容追加到文件后面。

/**
 * 插入文件指定位置的指定内容
 * @param filePath 文件路径
 * @param pos  插入文件的指定位置
 * @param insertContent 插入文件中的内容
 * @throws IOException
 */
  public static void insert(String filePath,long pos,String insertContent)throws IOException{
      RandomAccessFile raf=null;
      File tmp=File.createTempFile("tmp",null);
      tmp.deleteOnExit();
      try {
          // 以读写的方式打开一个RandomAccessFile对象
          raf = new RandomAccessFile(new File(filePath), "rw");
          //创建一个临时文件来保存插入点后的数据
          FileOutputStream fileOutputStream = new FileOutputStream(tmp);
          FileInputStream fileInputStream = new FileInputStream(tmp);
          //把文件记录指针定位到pos位置
          raf.seek(pos);
          raf.seek(pos);
          //------下面代码将插入点后的内容读入临时文件中保存-----
          byte[] bbuf = new byte[64];
          //用于保存实际读取的字节数据
          int hasRead = 0;
          //使用循环读取插入点后的数据
          while ((hasRead = raf.read(bbuf)) != -1) {
              //将读取的内容写入临时文件
              fileOutputStream.write(bbuf, 0, hasRead);
          }
          //-----下面代码用于插入内容 -----
          //把文件记录指针重新定位到pos位置
          raf.seek(pos);
          //追加需要插入的内容
          raf.write(insertContent.getBytes());
          //追加临时文件中的内容
          while ((hasRead = fileInputStream.read(bbuf)) != -1) {
              //将读取的内容写入临时文件
              raf.write(bbuf, 0, hasRead);
          }
      }catch (Exception e){
          throw  e;
      }
  }
    public  static void main(String[] args)throws IOException {
        String filePath="E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\Test.txt";
        insert(filePath,1000,"插入指定位置指定内容");
    }

 



 

 
 
 
 
 

 

posted @ 2021-06-22 16:10  邓维-java  阅读(2069)  评论(0编辑  收藏  举报