2

I am taking a total number of line as a user input and then I am deleting those numbers of l ine from the file.

I saw this learn.perl.org/faq/perlfaq5.html#How-do-I-count-the-number-of-lines-in-a-file- and then I tired the below simple logic.

Logic:

  1. Get the Total number of lines
  2. Subtracts it by the numbers entered by user
  3. print the lines

Here is my code :

#!/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;      
}

Well, neither i am getting any error in code nor any out put ? why I just don't know.

Can any one give me some suggestion.

Pavel Vlasov
  • 3,455
  • 21
  • 21
Maverick
  • 575
  • 1
  • 11
  • 27

10 Answers10

12

A Perl solution that's efficient for large files requires the use of 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 $!;
  • This does not read the whole file twice (unlike the OP's method).
  • This does not read the whole file (unlike the Tie::File solutions).
  • This does not read the whole file into memory.
JRFerguson
  • 7,426
  • 2
  • 32
  • 36
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • "This does not read the whole file (unlike the Tie::File solutions)" => I read that "The Tie::File solution is the most efficient solution, at least for large files, because it doesn't have to read through the entire file to find the last line and doesn't read the entire file into memory", see http://docstore.mik.ua/orelly/perl4/cook/ch08_11.htm – Georg May 24 '16 at 22:19
  • @Georg, Tie::File is rarely the most efficient solution. At best, it will be nearly as fast as a dedicated solution such as File::ReadBackwards because of all of its overhead. But that's not the case here. The proposed solution uses `$#lines`, which necessarilly causes the entire file to be read. I was hoping that `pop(@lines) for 1..10;` or `delete($lines[-1]) for 1..10;` would be nearly as fast as the F::RB solution, but i used `strace` to confirm that they read the entire file too (even though they could use the same approach as F::RB). – ikegami May 24 '16 at 23:13
  • @Georg, And that's not even talking about memory usage. Tie::File keeps an index of the position of every line it encounters. `delete($lines[-1])` on an 8MiB file of 64-byte lines caused Tie::File to use up just over 4MiB of memory for these offsets!!! (These fall outside of the caches and buffers whose size you can control.) – ikegami May 24 '16 at 23:28
  • @Georg, That linked document is completely wrong.Tie::File does exactly the same thing as the snippet that precedes it, with a lot of extra CPU and memory overhead. – ikegami May 24 '16 at 23:29
  • @Georg, Gah, if your average line is short (e.g. as is the case for source code), Tie::File can actually use up more memory for its offsets alone than the entire file!!! [Example](http://pastebin.com/Xd84gCAS) – ikegami May 24 '16 at 23:41
6

Yet another way is to use 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;

This has the advantage of not loading the file into memory while acting like it does.

JRFerguson
  • 7,426
  • 2
  • 32
  • 36
  • Note: This reads the entire file, and creates an index of every line in memory. My solution does neither. – ikegami Oct 02 '12 at 19:55
4

Here a solution that passes through a stream and prints all but the last n lines where n is a command line argument:

#!/usr/bin/perl

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

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

or the one-liner version:

perl -ne'BEGIN{$n=shift@ARGV}push@c,$_;print shift@c if@c>$n' NUMBER
amon
  • 57,091
  • 2
  • 89
  • 149
2

After finishing reading from IN, you have to reopen it or seek IN, 0, 0 to reset its position. You also have to set $. to zero again.

Also, the final condition should be changed to unless $. > $printed_line so you skip all the lines over the threshold.

choroba
  • 231,213
  • 25
  • 204
  • 289
  • @ choroba . I tried with seek function i mean I have added seek(IN, 0, 0) and i made $.=0; Now out put is coming but it is not removing last 10 lines I mean it is printing whole line as it is. – Maverick Oct 02 '12 at 19:36
1

Just read the file in reverse and delete the first n lines: -

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

Note: This loads the entire file into memory.

ikegami
  • 367,544
  • 15
  • 269
  • 518
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
1

The "more fun" answer: use Tie::File!

use strict;
use warnings;

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

$#file -= 10;
Joel Berger
  • 20,180
  • 5
  • 49
  • 104
  • Note: This reads the entire file, and creates an index of every line in memory. My solution does neither. – ikegami Oct 02 '12 at 19:54
1

You could just buffer the last 10 lines and then not print out the remaining 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, $_;
    }
}

You could also do this with 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;
Axeman
  • 29,660
  • 2
  • 47
  • 102
0

Easy with a C like for :

#!/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;
ikegami
  • 367,544
  • 15
  • 269
  • 518
Gilles Quénot
  • 173,512
  • 41
  • 224
  • 223
  • _GLOBAL_ filehandles are substituted by _local_ filehandles, it's safer & it's a best practice. See http://perldoc.perl.org/perlopentut.html#Indirect-Filehandles – Gilles Quénot Oct 02 '12 at 19:18
0
my $num_lines = 10;
my $qfn       = 'file.txt';

system('head', '-n', -$num_lines, '--', $qfn);
die "Error" if $?;
ikegami
  • 367,544
  • 15
  • 269
  • 518
0
  #
  # 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
  #
Yordan Georgiev
  • 5,114
  • 1
  • 56
  • 53