JAVA使用NIO技术按行读写大文件并且完美解决中文乱码问题

假设我们一次读取的字节是从下图的start到end,因为结尾是汉字,所以有几率出现上述的情况。

解决方法如下:将第9行这半行(第9行阴影的部分)跟上一次读取留下来的半行(第9行没阴影的部分)按顺序存放在字节数组,然后转成字符串;中间第10行到第17行正常转换成字符串;第18行这半行(第18行阴影的部分)留着跟下一次读取的第1行(第18行没阴影的部分)连接成一行,因为是先拼接成字节数组再转字符串,因此不会出现乱码的情况。

package com.chillax.imp;  
  
import java.io.File;  
import java.io.IOException;  
import java.io.RandomAccessFile;  
import java.nio.ByteBuffer;  
import java.nio.channels.FileChannel;  
import java.util.ArrayList;  
import java.util.Date;  
import java.util.List;  
  
/** 
 * NIO读取百万级别文件 
 * @author Chillax 
 * 
 */  
public class NIO {  
  
    public static void main(String args[]) throws Exception {  
  
        int bufSize = 1000000;//一次读取的字节长度  
        File fin = new File("D:\\test\\20160622_627975.txt");//读取的文件  
        File fout = new File("D:\\test\\20160622_627975_1.txt");//写出的文件  
        Date startDate = new Date();  
        FileChannel fcin = new RandomAccessFile(fin, "r").getChannel();  
        ByteBuffer rBuffer = ByteBuffer.allocate(bufSize);  
  
        FileChannel fcout = new RandomAccessFile(fout, "rws").getChannel();  
        ByteBuffer wBuffer = ByteBuffer.allocateDirect(bufSize);  
  
        readFileByLine(bufSize, fcin, rBuffer, fcout, wBuffer);  
        Date endDate = new Date();  
          
        System.out.print(startDate+"|"+endDate);//测试执行时间  
        if(fcin.isOpen()){  
            fcin.close();  
        }  
        if(fcout.isOpen()){  
            fcout.close();  
        }  
    }  
  
    public static void readFileByLine(int bufSize, FileChannel fcin,  
            ByteBuffer rBuffer, FileChannel fcout, ByteBuffer wBuffer) {  
        String enter = "\n";  
        List<String> dataList = new ArrayList<String>();//存储读取的每行数据  
        byte[] lineByte = new byte[0];  
          
        String encode = "GBK";  
//      String encode = "UTF-8";  
        try {  
            //temp:由于是按固定字节读取,在一次读取中,第一行和最后一行经常是不完整的行,因此定义此变量来存储上次的最后一行和这次的第一行的内容,  
            //并将之连接成完成的一行,否则会出现汉字被拆分成2个字节,并被提前转换成字符串而乱码的问题  
            byte[] temp = new byte[0];  
            while (fcin.read(rBuffer) != -1) {//fcin.read(rBuffer):从文件管道读取内容到缓冲区(rBuffer)  
                int rSize = rBuffer.position();//读取结束后的位置,相当于读取的长度  
                byte[] bs = new byte[rSize];//用来存放读取的内容的数组  
                rBuffer.rewind();//将position设回0,所以你可以重读Buffer中的所有数据,此处如果不设置,无法使用下面的get方法  
                rBuffer.get(bs);//相当于rBuffer.get(bs,0,bs.length()):从position初始位置开始相对读,读bs.length个byte,并写入bs[0]到bs[bs.length-1]的区域  
                rBuffer.clear();  
                  
                int startNum = 0;  
                int LF = 10;//换行符  
                int CR = 13;//回车符  
                boolean hasLF = false;//是否有换行符  
                for(int i = 0; i < rSize; i++){  
                    if(bs[i] == LF){  
                        hasLF = true;  
                        int tempNum = temp.length;  
                        int lineNum = i - startNum;  
                        lineByte = new byte[tempNum + lineNum];//数组大小已经去掉换行符  
                          
                        System.arraycopy(temp, 0, lineByte, 0, tempNum);//填充了lineByte[0]~lineByte[tempNum-1]  
                        temp = new byte[0];  
                        System.arraycopy(bs, startNum, lineByte, tempNum, lineNum);//填充lineByte[tempNum]~lineByte[tempNum+lineNum-1]  
                          
                        String line = new String(lineByte, 0, lineByte.length, encode);//一行完整的字符串(过滤了换行和回车)  
                        dataList.add(line);  
//                      System.out.println(line);  
                        writeFileByLine(fcout, wBuffer, line + enter);  
                          
                        //过滤回车符和换行符  
                        if(i + 1 < rSize && bs[i + 1] == CR){  
                            startNum = i + 2;  
                        }else{  
                            startNum = i + 1;  
                        }  
                          
                    }  
                }  
                if(hasLF){  
                    temp = new byte[bs.length - startNum];  
                    System.arraycopy(bs, startNum, temp, 0, temp.length);  
                }else{//兼容单次读取的内容不足一行的情况  
                    byte[] toTemp = new byte[temp.length + bs.length];  
                    System.arraycopy(temp, 0, toTemp, 0, temp.length);  
                    System.arraycopy(bs, 0, toTemp, temp.length, bs.length);  
                    temp = toTemp;  
                }  
            }  
            if(temp != null && temp.length > 0){//兼容文件最后一行没有换行的情况  
                String line = new String(temp, 0, temp.length, encode);  
                dataList.add(line);  
//              System.out.println(line);  
                writeFileByLine(fcout, wBuffer, line + enter);  
            }  
        } catch (IOException e) {  
            e.printStackTrace();  
        }   
    }  
  
    /** 
     * 写到文件上 
     * @param fcout 
     * @param wBuffer 
     * @param line 
     */  
    @SuppressWarnings("static-access")  
    public static void writeFileByLine(FileChannel fcout, ByteBuffer wBuffer,  
            String line) {  
        try {  
            fcout.write(wBuffer.wrap(line.getBytes("UTF-8")), fcout.size());  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
}  

猜你喜欢

转载自www.cnblogs.com/cnndevelop/p/12197363.html