zoukankan      html  css  js  c++  java
  • 【Java NIO的深入研究2】RandomAccessFile的使用

    RandomAccessFile

    RandomAccessFile是用来访问那些保存数据记录的文件的,你就可以用seek( )方法来访问记录,并进行读写了。这些记录的大小不必相同;但是其大小和位置必须是可知的。但是该类仅限于操作文件。

    RandomAccessFile不属于InputStream和OutputStream类系的。实际上,除了实现DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系毫不相干,甚至不使用InputStream和OutputStream类中已经存在的任何功能;它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是从零开始写的。这可能是因为RandomAccessFile能在文件里面前后移动,所以它的行为与其它的I/O类有些根本性的不同。总而言之,它是一个直接继承Object的,独立的类。

    基本上,RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream结合起来,再加上它自己的一些方法,比如定位用的getFilePointer( ),在文件里移动用的seek( ),以及判断文件大小的length( )、skipBytes()跳过多少字节数。此外,它的构造函数还要一个表示以只读方式("r"),还是以读写方式("rw")打开文件的参数 (和C的fopen( )一模一样)。它不支持只写文件。

    只有RandomAccessFile才有seek搜寻方法,而这个方法也只适用于文件。BufferedInputStream有一个mark( )方法,你可以用它来设定标记(把结果保存在一个内部变量里),然后再调用reset( )返回这个位置,但是它的功能太弱了,而且也不怎么实用。

    RandomAccessFile的绝大多数功能,但不是全部,已经被JDK 1.4的nio的"内存映射文件(memory-mapped files)"给取代了,你该考虑一下是不是用"内存映射文件"来代替RandomAccessFile了。

    现有如下的一个需求,向已存在1G数据的txt文本里末尾追加一行文字,内容如下“Lucene是一款非常优秀的全文检索库”。可能大多数朋友会觉得这个需求很easy,说实话,确实easy,然后XXX君开始实现了,直接使用Java中的流读取了txt文本里原来所有的数据转成字符串后,然后拼接了“Lucene是一款非常优秀的全文检索库”,又写回文本里了,至此,大功告成。后来需求改了,向5G数据的txt文本里追加了,结果XXX君傻了,他内存只有4G,如果强制读取所有的数据并追加,会报内存溢出的异常。

    其实上面的需求很简单,如果我们使用JAVA IO体系中的RandomAccessFile类来完成的话,可以实现零内存追加。其实这就是支持任意位置读写类的强大之处。

    RandomAccessFile是Java中输入,输出流体系中功能最丰富的文件内容访问类,它提供很多方法来操作文件,包括读写支持,与普通的IO流相比,它最大的特别之处就是支持任意访问的方式,程序可以直接跳到任意地方来读写数据。

    如果我们只希望访问文件的部分内容,而不是把文件从头读到尾,使用RandomAccessFile将会带来更简洁的代码以及更好的性能。

    下面来看下RandomAccessFile类中比较重要的2个方法,其他的和普通IO类似,在这里,就不详细说明了。

    方法名                     作用

    getFilePointer()   返回文件记录指针的当前位置

    seek(long pos)    将文件记录指针定位到pos的位置

    1.功能one,读取任意位置的数据

    2.功能two,追加数据

    3.功能three,任意位置插入数据

    public class Random {
        public static void main(String[] args) {  
            String path="randomtest.txt";         
             int seekPointer=20;  
             randomRed(path,seekPointer);//读取的方法   
             randomWrite(path);//追加写的方法  
            //insert(path, 23, "
    lucene是一个优秀的全文检索库");  
             
        } 
        public static void randomRed(String path,int pointe){  
            try{  
               
                RandomAccessFile raf=new RandomAccessFile(path, "r");  
                //获取RandomAccessFile对象文件指针的位置,初始位置是0  
                System.out.println("RandomAccessFile文件指针的初始位置:"+raf.getFilePointer());  
                raf.seek(pointe);//移动文件指针位置  
                byte[]  buff=new byte[1024];  
                //用于保存实际读取的字节数  
                int hasRead=0;  
                //循环读取  
                while((hasRead=raf.read(buff))>0){  
                    //打印读取的内容,并将字节转为字符串输入  
                    System.out.println(new String(buff,0,hasRead));  
                      
                }  
                              
            }catch(Exception e){  
                e.printStackTrace();  
            }  
    
        }  
        public static void randomWrite(String path){  
            try{  
                /**以读写的方式建立一个RandomAccessFile对象**/  
                RandomAccessFile raf=new RandomAccessFile(path, "rw");  
                  
                //将记录指针移动到文件最后  
                raf.seek(raf.length());  
                raf.write("我是追加的 
    ".getBytes());  
                  
            }catch(Exception e){  
                e.printStackTrace();  
            }  
        }  
        /** 
         * 实现向指定位置 
         * 插入数据 
         * @param fileName 文件名 
         * @param points 指针位置 
         * @param insertContent 插入内容 
         * **/  
        public static void insert(String fileName,long points,String insertContent){  
            try{  
            File tmp=File.createTempFile("tmp", null);  
            tmp.deleteOnExit();//在JVM退出时删除  
              
            RandomAccessFile raf=new RandomAccessFile(fileName, "rw");  
            //创建一个临时文件夹来保存插入点后的数据  
            FileOutputStream tmpOut=new FileOutputStream(tmp);  
            FileInputStream tmpIn=new FileInputStream(tmp);  
            raf.seek(points);  
            /**将插入点后的内容读入临时文件夹**/  
              
            byte [] buff=new byte[1024];  
            //用于保存临时读取的字节数  
            int hasRead=0;  
            //循环读取插入点后的内容  
            while((hasRead=raf.read(buff))>0){  
                // 将读取的数据写入临时文件中  
                tmpOut.write(buff, 0, hasRead);  
            }  
              
            //插入需要指定添加的数据  
            raf.seek(points);//返回原来的插入处  
            //追加需要追加的内容  
            raf.write(insertContent.getBytes());  
            //最后追加临时文件中的内容  
            while((hasRead=tmpIn.read(buff))>0){  
                raf.write(buff,0,hasRead);  
            }  
            }catch(Exception e){  
                e.printStackTrace();  
            }  
        }   
    
    }

    运行结果:

    最初randomtest.txt是这样的:

      采用randomWrite(path);追加后txt文件是这样的:

    采用insert(path, 23, " lucene是一个优秀的全文检索库");  txt文件是这样的:

  • 相关阅读:
    用实例来说明linux命令sort的用法
    shell 编程入门
    VMware sphere的使用
    linux进阶
    Windows系统下的TCP参数优化
    RFID Technology(上)——简介、市场应用与前景、工作原理
    RFID Technology(下)——面临的风险、安全与隐私策略
    TCP连接的状态与关闭方式,及其对Server与Client的影响
    Java Map遍历方式的选择
    博客处女贴
  • 原文地址:https://www.cnblogs.com/guweiwei/p/6526268.html
Copyright © 2011-2022 走看看