【问题标题】:Deleting the last line of a file with Java用Java删除文件的最后一行
【发布时间】:2012-02-27 07:26:53
【问题描述】:

我有一个.txt 文件,我想用Java 处理它。我想删除它的最后一行。

我需要有关如何实现这一点的想法,而不必将整个内容复制到另一个文件并忽略最后一行。有什么建议吗?

【问题讨论】:

  • 重要问题。这样的东西可以处理具有低 RAM 使用率和低 IO 操作的大文件。

标签: java file-io text-processing


【解决方案1】:

通过使用 RandomAccessFile,您可以:

  • 使用方法 seek(long) 向前跳转并阅读这些行。但您不会确切知道跳跃应该有多大。
  • 要删除最后一行,您需要最后一行的开头位置,因此在读取每一行之前存储它们的文件指针位置(方法 getFilePointer())。使用 setLength(long) 删除到该位置。

代码应该是这样的:

    LinkedList<String> lines=null;
    int howMuch = 1; // one line to read and delete
    try{
        RandomAccessFile raf = new RandomAccessFile(inputFileName, "rw");

        System.out.println("File Length="+raf.length());

        long step = 20;  // here I advice to write average length of line
        long jump = raf.length()<step*howMuch?
                0:
                    raf.length()-step*howMuch; 
        raf.seek(jump);
        lines = new LinkedList<String>();
        LinkedList<Long> pos = new LinkedList<Long>();

        Entry<LinkedList<String>,LinkedList<Long>> rLinesRead = getRemainingLines(raf,
                new AbstractMap.SimpleEntry<LinkedList<String>,LinkedList<Long>> (lines,pos));
        while(rLinesRead.getKey().size()<howMuch){
            if(jump<step)
                if(jump<=0)
                    break;
                else{
                    jump=0;
                    raf.seek(jump);
                    rLinesRead=getRemainingLines(raf,rLinesRead);
                    break;
                }
            else
                jump=jump-step;
            raf.seek(jump);
            rLinesRead=getRemainingLines(raf,rLinesRead);
        }
        int originalSize=rLinesRead.getKey().size(); 
        lines=rLinesRead.getKey();
        pos=rLinesRead.getValue();
        for (int i=0;i<originalSize-howMuch;++i){
            lines.removeFirst();
            pos.removeFirst();
        }
        if(!pos.isEmpty())
            raf.setLength(pos.getFirst()); // before last(from EOF) returned fully read lines in file          
    }catch(Exception ex){
        ex.printStackTrace();
    } finally{
        try {               raf.close();            } catch (Exception e) {             e.printStackTrace();            }
    }
//returns line to EOF with their begin file pointer positions
private Entry<LinkedList<String>,LinkedList<Long>> getRemainingLines(RandomAccessFile raf,
        Entry<LinkedList<String>,LinkedList<Long>> linesAlreadyLoadedFromEnd) throws IOException{
    LinkedList<String> pLines = linesAlreadyLoadedFromEnd.getKey();
    LinkedList<Long> pPos = linesAlreadyLoadedFromEnd.getValue();

    long init=raf.getFilePointer();
    String str = raf.readLine();
    if(pPos.size()>0?pPos.getFirst()==0:false || str==null)
        return linesAlreadyLoadedFromEnd;

    LinkedList<String> lines = new LinkedList<String>();
    LinkedList<Long> pos = new LinkedList<Long>();
    if(init==0L ){              
        lines.add(str);
        pos.add(0L);
    }
    Long tmpPos = raf.getFilePointer();
    while ((str = raf.readLine())!=null && !pPos.contains(tmpPos)){
        lines.add(str);
        pos.add(tmpPos);
        tmpPos = raf.getFilePointer();
    }
    pLines.addAll(0,lines);
    pPos.addAll(0,pos);
    return new AbstractMap.SimpleEntry<LinkedList<String>,LinkedList<Long>> (pLines,pPos);
}

【讨论】:

    【解决方案2】:

    您可以通过扫描文件找到最后一行的开头,然后使用FileChannel.truncateRandomAccessFile.setLength 截断它。

    【讨论】:

    • 这个只能以字节为单位,编码相关的大小不用你自己计算吗?
    • 显然,这取决于编码。在 ASCII 或 Latin-1 编码(或其他每个字符 1 个字节的编码)中按字节搜索换行符 (\0x0a) 是安全的。我相信对 UTF-8 执行此操作也是安全的,因为所有多字节序列都设置了高位。它会失败,例如 UTF-16。
    • 你是对的,对于包括 UTF-8 在内的典型编码,这应该不是问题(只要你可以定义“行尾”,即 ;-))。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-25
    • 1970-01-01
    • 1970-01-01
    • 2012-03-31
    • 1970-01-01
    相关资源
    最近更新 更多