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

来源:互联网 发布:mac奶瓶粉底液好用吗 编辑:程序博客网 时间:2024/06/06 12:57

这一次我们着重来讨论如何用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();        }    }}
原创粉丝点击