前不久看到论坛里那个不用临时文件,将2G的文本文件逆序的帖子,我试着实践了一下,方法一是采用普通的RandomAccessFile的不停seek开头和结束,运行成功,但是处理2G文件时非常之慢
方法二是采用了nio的FileChannel的内存地址映射,运行效率明显比方法一要快,但是在最后一步把重复了两次的文件截去一半的时候总是处理不好请各位大侠为我捉刀,看看如何改善final.txt是那个2G的文本,内容类似于"?吗好你?abcde还可以"
输出的期待应该是"以可还edcba?你好吗?"
现在的输出是"以可还edcba?你好吗?                ",后面多了很多空格import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;/**
 *
 * @author Administrator
 * reverse the file character by character, without temporary file
 * can deal with both single-byte character and dual-bytes character
 */
public class FileReverserFinalII {
    private static int interval = 10000;
    private static String sourceFile = "final.txt";
    private static RandomAccessFile writer,reader;
    
    public static void main(String[] args){
        try {
            MappedByteBuffer out = null;
            
            writer = new RandomAccessFile(sourceFile,"rw");
            FileChannel source = new RandomAccessFile(sourceFile,"rw").getChannel();
            long length = source.size();
            long currentLength = length;
            writer.seek(currentLength);
            
            boolean end = false;
//            long index = 0;
            long start = currentLength - interval;
            long distance = interval;
            do{
                if(start<=0){
                    start = 0;
                    distance = currentLength;
                    end = true;
                }
                out = source.map(FileChannel.MapMode.READ_ONLY,start,distance);
                
                byte b;
                char c;
                
                for(int i=out.capacity()-1;i>=0;){
                    b = out.get(i);
                    if(b<0){
                        i--;
                        c = out.getChar(i);
                        writer.writeChar(c);
                    }else{
                        writer.writeByte(b);
                    }
                    i--;
                }
                if(end)
                    break;
                else{
                    start = start - interval;
                    currentLength = currentLength - interval;
                }
            }while(true);
            
            start = length;
            distance = interval;
            writer.seek(0);
            do{
                if(start>=(2*length-interval)){
                    distance = 2*length-start;
                    end = true;
                }
                out = source.map(FileChannel.MapMode.READ_ONLY,start,distance);
                
                byte b;
                char c;
                
                for(int i=0;i<out.capacity()-1;){
                    b = out.get(i);
                    if(b<0){
                        c = out.getChar(i);
                        writer.writeChar(c);
                        i+=2;
                    }else{
                        writer.writeByte(b);
                        i++;
                    }
                }
                if(end)
                    break;
                else{
                    start = start + interval;
                }
            }while(true);
            
            /**
             * 最有问题的一段
             * 用来将后半段文件截去
             */
            currentLength = length;
            writer.seek(currentLength);
            while(writer.getFilePointer()<length*2){
                writer.write(" ".getBytes("UTF-8"));
            }
            source.close();
            writer.close();
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        } 
    }
}

解决方案 »

  1.   

    我尝试着使用RandomAccessFile的setLength(一半的长度)来截断文件,这在我没有用到内存地址映射时可以很好的工作,但是现在则会报错“java.io.IOException: 请求的操作无法在使用用户映射区域打开的文件上执行。”
      

  2.   

    我在处理一个问题的时候遇到了一个类似的问题,不过采用垃圾收集机制解决了无法truncate的问题,可能对你有所借鉴我用文本存储数据记录,删除其中记录的时候,采用RandomAccessFile,并打开了FileChannel,将文件内容可读写的映射成MappedByteBuffer,然后采用正则表达式来查找并替换删除
    因为删除后的内容比原有内容小了,所以重新用FileChannel写回去后,也会出现原有文件的末端出现在新文件的尾端的情况,跟你的后面有空格类似,我在映射内存后执行truncate也出现了问题,现解决方法如下:
    将映射内存后得到的buff句柄都置成null,然后强制执行一次System.gc();然后在用FileChannel写入文件内容并执行truncate就可以了