【问题标题】:How can I delete the last 10 lines of a file in perlperl 如何删除文件的最后 10 行
【发布时间】:2012-10-29 02:22:46
【问题描述】:

我将总行数作为用户输入,然后从文件中删除这些行数。

我看到了这个 learn.perl.org/faq/perlfaq5.html#How-do-I-count-the-number-of-lines-in-a-file- 然后我厌倦了下面的简单逻辑。

逻辑:

  1. 获取总行数
  2. 减去用户输入的数字
  3. 打印线条

这是我的代码:

#!/usr/bin/perl -w
use strict;

open IN, "<", "Delete_line.txt"
    or die " Can not open the file $!";
open OUT, ">", "Update_delete_line.txt" 
    or die "Can not write in the file $!";

my ($total_line, $line, $number, $printed_line);

print"Enter the number of line to be delete\n";
$number = <STDIN>;

while ($line = <IN>) {

    $total_line = $.;  # Total number of line in the file
}

$printed_line = $total_line - $number;

while ($line = <IN>) {

    print OUT $line unless $.== $printed_line;      
}

好吧,我既没有收到任何代码错误,也没有收到任何输出?为什么我只是不知道。

谁能给我一些建议。

【问题讨论】:

  • 你不能只反向读取文件吗?并删除前n行??

标签: perl


【解决方案1】:

对大文件有效的 Perl 解决方案需要使用 File::ReadBackwards

use File::ReadBackwards qw( );

my $num_lines = 10;
my $qfn = 'file.txt';

my $pos = do {
   my $fh = File::ReadBackwards->new($qfn)
      or die $!;
   $fh->readline() for 1..$num_lines;
   $fh->tell()
};

truncate($qfn, $pos)
   or die $!;
  • 这不会两次读取整个文件(与 OP 的方法不同)。
  • 这不会读取整个文件(与Tie::File 解决方案不同)。
  • 这不会将整个文件读入内存。

【讨论】:

  • “这不会读取整个文件(与 Tie::File 解决方案不同)” => 我读到“Tie::File 解决方案是最有效的解决方案,至少对于大文件而言,因为它不必通读整个文件来查找最后一行,也不会将整个文件读入内存”,请参阅docstore.mik.ua/orelly/perl4/cook/ch08_11.htm
  • @Georg,Tie::File 很少是最有效的解决方案。充其量,它几乎与 File::ReadBackwards 等专用解决方案一样快,因为它的所有开销。但这里不是这样。建议的解决方案使用$#lines,这必然会导致读取整个文件。我希望 pop(@lines) for 1..10;delete($lines[-1]) for 1..10; 几乎与 F::RB 解决方案一样快,但我使用 strace 确认他们也读取了整个文件(即使他们可以使用与 F 相同的方法::RB)。
  • @Georg,这甚至没有谈论内存使用情况。 Tie::File 保留它遇到的每一行的位置索引。 delete($lines[-1]) 在 64 字节行的 8MiB 文件上导致 Tie::File 为这些偏移量使用了超过 4MiB 的内存!!! (这些不属于您可以控制大小的缓存和缓冲区。)
  • @Georg,链接的文档完全错误。Tie::File 与之前的 sn-p 执行完全相同的操作,但需要大量额外的 CPU 和内存开销。
  • @Georg,Gah,如果您的平均行很短(例如源代码的情况),Tie::File 实际上可以单独使用其偏移量而不是整个文件! ! Example
【解决方案2】:

还有一种方法是使用Tie::File

#!/usr/bin/env perl
use strict;
use warnings;
use Tie::File;
tie my @lines, 'Tie::File', 'myfile' or die "$!\n";
$#lines -= 10;
untie @lines;

这样做的好处是将文件加载到内存中而像它那样操作。

【讨论】:

  • 注意:这会读取整个文件,并为内存中的每一行创建一个索引。我的解决方案都没有。
【解决方案3】:

这是一个通过流并打印除最后 n 行之外的所有行的解决方案,其中 n 是命令行参数:

#!/usr/bin/perl

my @cache;
my $n = shift @ARGV;

while(<>) {
    push @cache, $_;
    print shift @cache if @cache > $n;
}

或单行版本:

perl -ne'BEGIN{$n=shift@ARGV}push@c,$_;print shift@c if@c>$n' NUMBER

【讨论】:

  • +1,这是最好的答案(除非需要就地编辑)。
【解决方案4】:

从IN读完后,你必须重新打开它或seek IN, 0, 0重置它的位置。您还必须再次将$. 设置为零。

此外,最终条件应更改为unless $. &gt; $printed_line,以便您跳过所有超过阈值的行。

【讨论】:

  • @choroba 。我尝试使用 seek 功能,我的意思是我添加了 seek(IN, 0, 0) 并且我做了 $.=0;现在输出来了,但它没有删除最后 10 行我的意思是它正在打印整行。
【解决方案5】:

“更有趣”的答案:使用Tie::File

use strict;
use warnings;

use Tie::File;
tie my @file, 'Tie::File', 'filename' or die "$!";

$#file -= 10;

【讨论】:

  • 注意:这会读取整个文件,并为内存中的每一行创建一个索引。我的解决方案都没有。
【解决方案6】:

只需反向读取文件并删除前n行:-

open my $filehandle, "<", "info.txt";
my @file = <$filehandle>;
splice(@file, -10);
print @file;

注意:这会将整个文件加载到内存中。

【讨论】:

  • 这也会向后打印文件。
  • 所以。您仍在向后打印文件(最后 10 行除外)。
  • @mob.. O.o.. Nice catch mob.. 那么这可能只应该用于删除行..
【解决方案7】:

您可以只缓冲最后 10 行,然后不打印剩余的 10 行。

use English qw<$INPLACE_EDIT>;

{   local @ARGV         = $name_of_file_to_edit;
    local $INPLACE_EDIT = '.bak';
    my @buffer;
    for ( 1..$num_lines_to_trim ) { 
        push @buffer, <>;
    }

    while ( <> ) { 
        print shift @buffer;
        push @buffer, $_;
    }
}

您也可以使用File::Slurp::edit_file_lines

my @buffer;
my $limit_reached = 0;
edit_file_lines {  
    push @buffer, $_;
    return ( $limit_reached ||= @buffer > $num_lines_to_trim ) ? shift @buffer
         :                                                       ''
         ;
} $name_of_file;

【讨论】:

    【解决方案8】:
    my $num_lines = 10;
    my $qfn       = 'file.txt';
    
    system('head', '-n', -$num_lines, '--', $qfn);
    die "Error" if $?;
    

    【讨论】:

      【解决方案9】:

      使用像 for 这样的 C 很容易:

      #!/usr/bin/perl -w
      use strict;
      
      open(my $in,"<","Delete_line.txt") or die "Can not open the file $!";
      open(my $out,">","Update_delete_line.txt") or die"Can not write in the file $!";
      
      print"Enter the number of lines to be delete\n";
      my $number=<STDIN>;
      
      my @file = <$in>;
      
      for (my $i = 0; $i < $#file - $number + 1; $i++) {
          print $out $file[$i];
      }
      
      close $in;
      close $out;
      

      【讨论】:

      【解决方案10】:
        #
        # Reads a file trims the top and the bottom of by passed num of lines
        # and return the string 
        # stolen from : http://stackoverflow.com/a/9330343/65706
        # usage :       
        # my $StrCatFile = $objFileHandler->ReadFileReturnTrimmedStrAtTopBottom ( 
        #       $FileToCat , $NumOfRowsToRemoveAtTop , $NumOfRowsToRemoveAtBottom) ; 
        sub ReadFileReturnTrimmedStrAtTopBottom {
      
           my $self = shift ; 
           my $file = shift ; 
           my $NumOfLinesToRemoveAtTop = shift ; 
           my $NumOfLinesToRemoveAtBottom = shift ; 
      
           my @cache ; 
           my $StrTmp = () ; 
           my $StrReturn = () ; 
           my $fh = () ; 
      
           open($fh, "<", "$file") or cluck (   "can't open file : $file for reading: $!" ) ;
           my $counter = 0;
           while (<$fh>) {
               if ($. >= $NumOfLinesToRemoveAtTop + 1) {
                   $StrTmp .= $_ ;
               }
           } 
           close $fh;
      
           my $sh = () ; 
           open( $sh, "<", \$StrTmp) or cluck(   "can't open string : $StrTmp for reading: $!" ) ;
           while(<$sh>) {
               push ( @cache, $_  ) ;
               $StrReturn .= shift @cache if @cache > $NumOfLinesToRemoveAtBottom;
           }
           close $sh ; 
           return $StrReturn ; 
        } 
        #eof ReadFileReturnTrimmedStrAtTopBottom
        #
      

      【讨论】:

        猜你喜欢
        • 2011-07-06
        • 2011-04-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-20
        相关资源
        最近更新 更多