NIO学习笔记(四)——利用NIO替换大文件内容

本文探讨如何使用Java的NIO非阻塞I/O来高效替换大文件中的内容,避免传统IO方法带来的资源浪费。通过建立读写通道,判断并替换目标字符,调整缓冲区偏移量,实现边读边改,确保正确替换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这一次我们着重来讨论如何用NIO来修改较大文件里的内容。如果是较小的文件,利用传统IO来进行修改并不难,思路是:
(1)使用输入流读出文件内容。
(2)读的过程中判断符合替换条件的内容。
(3)如果符合,把这些内容写入到输出流,这个输出流最后会写入文件的原始位置。
其实这个过程相当于重新写了一份文件,然后将原来的文件替换掉。然后在替换的过程中,去处理数据。
但是一旦文件较大,而且改动又很小,这种模式其实是十分浪费资源的。比如说我要把一个较大文件里的所有某种所有字符,修改为另一种字符。有没有一种方法能够边读边换呢?

我们先把整体的框架写出来:

/**
 * 利用NIO替换大文件内容。
 */
public class NIOFileContentReplace {
    public static void main(String[] args) {
        //创建channel对象
        try(FileChannel channel = FileChannel.open(Paths.get("f:/test.txt"), StandardOpenOption.READ,StandardOpenOption.WRITE);) {
            //读取文件内容,读取buffer
            ByteBuffer buffer = ByteBuffer.allocate(4);
            int flag = channel.read(buffer);
            while(-1 != flag) {
                buffer.flip();
                //替换内容

                //写回文件
                buffer.clear();
                flag = channel.read(buffer);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这部分框架已经成为一种定式,加以练习即可掌握。

替换内容的时候,我们要做的就是把这部分内容取出来。举个例子,将文本里所有的“你”替换成“我”:

//替换内容
String content = new String(buffer.array(), "GBK");
String newContent = content.replace('你', '我');

替换的过程中,有可能有的语句中没有“你”字。那么我们做一个判断,判断的依据是,遍历buffer,有“你”字我们才执行内容替换。

buffer.flip();
CharBuffer charBuffer = Charset.forName("GBK").decode(buffer);
while(charBuffer.hasRemaining()) {
    if('你' == charBuffer.get()) {
    //替换内容
    String content = new String(buffer.array(), "GBK");
    String newContent = content.replace('你', '我');
    //写回文件
    }
}
buffer.clear();
flag = channel.read(buffer);

接着进行写回文件,写回文件它需要依赖channel.write方法,write需要一个buffer。我们将这个newContent声明成一个数组,构建一个ByteBuffer:

//写回文件                      channel.write(ByteBuffer.wrap(newContent.getBytes()));

那么是不是这样写就可以了呢?经过测试,显然,并不是我们所希望的结果。它找出了这些“你”字,但是在替换的过程中出现了错位。之所以会出现这种情况,是因为读和写共用的都是一个channel。channel和buffer一样,都有position这个属性。这让channel具有了回头读的功能,传统IO则不具备这项功能,而恰恰这个功能,使得刚才没有得到正确的替换。channel在read的时候,position已经偏移了,而就着这个偏移值,导致出现错误。所以我们在查找到了指定字符之后,应该调整偏移量:

//重置channel的position,由于channel.read会影响这个值
channel.position(channel.position() - buffer.limit());

这样一来我们就可以利用NIO而且高效地得到正确的替换结果,全文代码如下:

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

/**
 * 利用NIO替换大文件内容。
 * @author 青葉
 *
 */
public class NIOFileContentReplace {
    public static void main(String[] args) {
        //创建channel对象
        try(FileChannel channel = FileChannel.open(Paths.get("f:/test.txt"), StandardOpenOption.READ,StandardOpenOption.WRITE);) {
            //读取文件内容,读取buffer
            ByteBuffer buffer = ByteBuffer.allocate(4);
            int flag = channel.read(buffer);
            while(-1 != flag) {
                buffer.flip();
                CharBuffer charBuffer = Charset.forName("GBK").decode(buffer);
                while(charBuffer.hasRemaining()) {
                    if('你' == charBuffer.get()) {
                        //替换内容
                        String content = new String(buffer.array(), "GBK");
                        String newContent = content.replace('你', '我');
                        //重置channel的position,由于channel.read会影响这个值
                        channel.position(channel.position() - buffer.limit());
                        //写回文件
                        channel.write(ByteBuffer.wrap(newContent.getBytes()));
                    }
                }
                buffer.clear();
                flag = channel.read(buffer);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值