【JAVA使用笔记】——大文件分割、内容处理

简述

对于提高大文件读写效率的方法网上数不胜数,我这里选择了使用NIO结合多线程处理的方式。

通过 io 的 RandomAccessFile 实现对大文件按行分片。

通过 nio 的 MappedByteBuffer 和 ByteBuffer 对缓冲区内的数据进行处理

通过nio 的 FileChannel,实现大文件的读写。

分片

实现在限制的分片大小(partitionSize)范围内,找出数据完整的切片,即行完整的切片。

大体逻辑是:

  • 确认分片起始位置(start)
  • 分片起始位置(start)+分片最大限制(partitionSize)得到待修正结束位置(preEnd)
  • 定位修正结束位置字符,向前遍历直到等于\r或\n
  • 以此作为分片结束位置(end)
  • 传递分片结束位置+1为下一次迭代开始位置

代码:

/**
     * 递归解决分区
     * @param result 结果集
     * @param filePointer 文件指针
     * @param start 本次开始位置
     * @param fileSize 文件大小
     * @param partitionSize 分区最大大小
     * @throws IOException 异常
     */
    private static void partitionHandle(LinkedList result,
                                        RandomAccessFile filePointer,
                                        long start,
                                        long fileSize,
                                        long partitionSize) throws IOException{
        //新建分区 设置分区开始
        FilePartition partition = new FilePartition();
        partition.setStart(start);
        //判断结束是否在文件大小范围内
        long preEnd = start + partitionSize;
        if(preEnd < fileSize){
            //不越界获取preEnd-1位置字符,不是\r或者\n就倒车
            filePointer.seek(preEnd-1);
            byte b = filePointer.readByte();
            while (b != 13 && b != 10){
                preEnd = preEnd -1;
                filePointer.seek(preEnd);
                b = filePointer.readByte();
            }
            partition.setEnd(preEnd);
            result.add(partition);
            partitionHandle(result,filePointer,preEnd+1,fileSize,partitionSize);
        } else {
            //越界则以fileSize-1作为最后结束
            partition.setEnd(fileSize-1);
            result.add(partition);
        }

读取

使用 RandomAccessFile 打开文件,获得其 MappedByteBuffer

RandomAccessFile source = new RandomAccessFile(sourcePath,"r");
// 打开读取内存映射
MappedByteBuffer input = source.getChannel().map(
                    FileChannel.MapMode.READ_ONLY,
                    partition.getStart(),
                    partition.getEnd()-partition.getStart());

RandomAccessFile 获得 FileChannel 在进而获得 MappedByteBuffer,使用只读模式,从分片开始,读取分片大小的数据进行映射。

使用 byte[] 建立缓冲区从映射中拿出需要的数据,变成可处理的字符串

byte[] block = new byte[buffer_size];
while (offset < input.capacity()) {
    int block_size = input.capacity() - offset >= buffer_size ? buffer_size : input.capacity() - offset;
    for(int idx=0; idx < block_size;idx ++){
        block[idx] = input.get(offset+idx);
    }
    String data = new String(block,0,block_size);
    String[] lines = data.split("\n");
    ... ...                
    offset = offset + buffer_size;
}

写入

使用追加写的 FileOutputStream 获得FileChannel,使用 ByteBuffer 写入

FileOutputStream passFOS = new FileOutputStream(goodPath,true);
FileChannel passFC = passFOS.getChannel();
ByteBuffer pass_buffer = ByteBuffer.wrap(block);
pass_buffer.put(block);
pass_buffer.flip();
passFC.write(pass_buffer);
passFOS.flush();

性能

文件大小1281651KB约1.22G

对文件内数据行包含‘男性’和包含‘女性’字样的数据进行分堆

分区大小 268435456 约256M

缓冲区大小 26843546 约25.6M

耗时 11773ms 速度约为106M/S

【JAVA使用笔记】——大文件分割、内容处理_第1张图片

你可能感兴趣的:(JAVA)