JAVA之NIO按行读取大文件

时间:2023-01-18 20:15:54

        做项目过程中遇到要解析100多M的TXT文件,并入库。用之前的FileInputStream、BufferedReader显然不行了,虽然readLine这方法可以直接按行读取,但是去读一个140M左右,68W条数据的文件时,不但耗时长而且会内存溢出,即你等不到读完68W条数据时就内存溢出了。所以得用NIO下面的相关对象及方法。

       用到 字节缓冲区(java.nio.ByteBuffer);用于读取、写入、映射和操作文件的通道( java.nio.channels.FileChannel);设置文本字条集(java.nio.charset.Charset);支持对随机存取文件的读取和写入(java.io.RandomAccessFile)。

       具体思路是:设置两个缓冲区,一大一小,大的缓冲区为每次读取的量,小的缓冲区存放每行的数据(确保大小可存放文本中最长的那行)。读取的时候判断是不是换行符13,是的话则返回一行数据,不是的话继续读取,直到读完文件。

       实现方法:

        FileChannel fc=raf.getChannel();

       //一次读取文件,读取的字节缓存数
       ByteBuffer fbb=ByteBuffer.allocate(1024*5);
        fc.read(fbb);
        fbb.flip();

     //每行缓存的字节   根据你的实际需求    

      ByteBuffer bb=ByteBuffer.allocate(500);

  

              //判断是否读完文件

public boolean hasNext() throws IOException {

        if(EOF)return false;
        if(fbb.position()==fbb.limit()){//判断当前位置是否到了缓冲区的限制
            if(readByte()==0)  return false;
        }
        while(true){
            if(fbb.position()==fbb.limit()){
                if(readByte()==0)  break;
            }
            byte a=fbb.get();
            if(a==13){
                if(fbb.position()==fbb.limit()){
                    if(readByte()==0)  break;
                }
                return true;
            }else{
                if (bb.position() < bb.limit()) {
                    bb.put(a);
                }else {
                    if(readByte()==0)  break;
                }
            }
        }
        return true;
    }

private int readByte() throws IOException{
        //使缓冲区做好了重新读取已包含的数据的准备:它使限制保持不变,并将位置设置为零。
        fbb.rewind();
        //使缓冲区做好了新序列信道读取或相对 get 操作的准备:它将限制设置为当前位置,然后将该位置设置为零。
        fbb.clear();
        if(this.fc.read(fbb)==-1){
            EOF=true;
            return 0;
        }else{
            fbb.flip();
            return fbb.position();
        }
    }

    public byte[] next(){
        bb.flip();

        //此处很重要,返回byte数组方便,行被分割的情况下合并,否则如果正好达到缓冲区的限制时,一个中文汉字被拆了两个字节,就会显示不正常
        byte tm[] = Arrays.copyOfRange(bb.array(), bb.position(), bb.limit());
        bb.clear();
        return tm;
    }