如何删除文件的最后N行?

时间:2008-12-05 23:34:26

标签: perl lines

有人可以提供一些关于如何从Perl文件中删除最后n行的提示吗?我有一个大约400 MB的非常大的文件,我想从中删除大约125,000个最后一行。

11 个答案:

答案 0 :(得分:13)

您可以使用Tie::File将文件作为数组处理。

use Tie::File;
tie (@File, 'Tie::File', $Filename);
splice (@File, -125000, 125000);
untie @File;

另一种方法是在shell中使用headwc -l

编辑: grepsedawk提醒我们-n的{​​{1}}选项,不需要head

head -n -125000 FILE > NEWFILE

答案 1 :(得分:6)

由于人们已经建议使用Tie :: Array,这可以很好地完成工作,如果您想手动完成,我将列出基本算法。有些草率,缓慢的方法可以很好地处理小文件。这是为大文件执行此操作的有效方法。

  1. 在结尾的第N行之前找到文件中的位置。
  2. 在该点之后截断所有内容(使用truncate())。
  3. 1是棘手的部分。我们不知道文件中有多少行或它们在哪里。一种方法是计算所有线,然后回到第N。这意味着我们每次都必须扫描整个文件。更高效的是从文件末尾向后读取。您可以使用read()执行此操作,但更容易使用File::ReadBackwards,它可以逐行倒退(同时仍然使用有效的缓冲读取)。

    这意味着您只读取了125,000行而不是整个文件。 truncate()应该是O(1)和原子,无论文件有多大,都几乎没有任何成本。它只是重置文件的大小。

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use File::ReadBackwards;
    
    my $LINES = 10;     # Change to 125_000 or whatever
    my $File = shift;   # file passed in as argument
    
    my $rbw = File::ReadBackwards->new($File) or die $!;
    
    # Count backwards $LINES or the beginning of the file is hit
    my $line_count = 0;
    until( $rbw->eof || $line_count == $LINES ) {
        $rbw->readline;
        $line_count++;
    }
    
    # Chop off everything from that point on.
    truncate($File, $rbw->tell) or die "Could not truncate! $!";
    

答案 2 :(得分:4)

你知道有多少行,或者有关于这个文件的其他任何线索?你是否必须反复这样做,或者只是一次?

如果我必须这样做一次,我会在vim中加载文件,查看最后一个行号,然后从我想要的最后一行删除直到结束:

:1234567,$d

一般的编程方式是两次通过:一次确定行数,然后一次除去行。

简单的方法是将正确的行数打印到新文件中。它在循环方面效率很高,可能还有一些磁盘颠簸,但大多数人都有很多这样的东西。 perlfaq5中的一些内容应该会有所帮助。你完成了工作,你可以继续生活。

while(  )
   {
   print $out;
   last if $. > $last_line_I_want;
   }

如果这是您必须做的事情或数据大小太大而无法重写它,您可以创建行和字节偏移的索引并将文件truncate()设置为正确的大小。保留索引时,您只需要发现新的行结尾,因为您已经知道从哪里开始。一些文件处理模块可以为您处理所有这些。

答案 3 :(得分:4)

我只是使用shell脚本来解决这个问题:

tac file | sed '1,125000d' | tac

(tac就像猫,但是以相反的顺序打印线条。作者:Jay Lepreau和David MacKenzie。部分GNU coreutils。)

答案 4 :(得分:3)

  1. 转到文件末尾:fseek
  2. 向后计数很多行
  3. 找出档案位置:ftell
  4. 将文件截断为该位置为length:ftruncate

答案 5 :(得分:0)

Schwern:您的脚本中的use Fnctl$rbw->get_handle行是否必要?另外,我建议在不返回true的情况下报告truncate错误。

- 道格拉斯·亨特(如果可以的话,他会评论那篇文章)

答案 6 :(得分:0)

试试此代码:

  

我的$ i = 0;
  sed -i'\ $ d'文件名while($ i ++< n);

反引号也会在那里,但我无法打印出来:(

答案 7 :(得分:0)

试试这个

:|dd of=urfile seek=1 bs=$(($(stat -c%s urfile)-$(tail -1 urfile|wc -c)))

答案 8 :(得分:0)

我的建议,使用ed

printf '$-125000,$d\nw\nq\n' | ed -s myHugeFile

答案 9 :(得分:0)

此示例代码将在扫描文件时保留最后10行的索引。然后它使用缓冲区中最早的索引来截断文件。这当然只有在您的系统上使用truncate时才有效。

#! /usr/bin/env perl
use strict;
use warnings;
use autodie;

open my $file, '+<', 'test.in'; # rw
my @list;
while(<$file>){
  if( @list <= 10 ){
    push @list, tell $file;
  }else{
    (undef,@list) = (@list,tell $file);
  }
}

seek $file, 0, 0;
truncate $file, $list[0] if @list;
close $file;

这样做的另一个好处是它只占用了最后十个索引和当前行的足够内存。

答案 10 :(得分:-1)

最有效的方法是寻找文件的末尾,然后逐步读取段,同时计算每个段中的换行符数,然后使用truncate(请参阅perldoc -f truncate)将其修剪下来。 CPAN上还有一个或两个模块用于向后读取文件。