Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

reading the file using the line number

by greatshots (Pilgrim)
on Nov 29, 2006 at 07:34 UTC ( [id://586632]=perlquestion: print w/replies, xml ) Need Help??

greatshots has asked for the wisdom of the Perl Monks concerning the following question:

dear monks,

I have a log file. I am not interested in reading all the lines of a log file. I am only interested in reading a specific lines of a file using line number. For example, If I would like to read only line numbers 6,1003,2965 from the log file, what is the best way I can write a code for this.
right now I am reading the whole file into an array and from the array I read the file.

my @log_file_contents = <FH>; map { $_ =~ s/[\r\n]//g' } @log_file_contents; print "$log_file_contents[6]\n"; print "$log_file_contents[1003]\n"; print "$log_file_contents[2965]\n";
Is there any better solution for this ?

Replies are listed 'Best First'.
Re: reading the file using the line number
by ikegami (Patriarch) on Nov 29, 2006 at 07:37 UTC

    Tie::File is simplest.

    tie my @array, 'Tie::File', $file_name or die("Unable to open file \"$file_name\": $!\n"); print($array[$_]) for 6, 1003, 2965;

    Tie::File can use a fair bit of memory (for its index, which is above and beyond what the memory argument limits), so you might want to write your own (faster, more memory efficient) solution.

    my %lines_of_interest = map { $_ => 1 } 6, 1003, 2965; open my $fh, '<', $file_name or die("Unable to open file \"$file_name\": $!\n"); my $num_lines = keys %lines_of_interest; while (<$fh>) { if ($lines_of_interest{$.}) { print; last unless --$num_lines; } }

    Update: Added Tie::File code.
    Update: Added key counting to exit loop sooner.

Re: reading the file using the line number
by davido (Cardinal) on Nov 29, 2006 at 07:43 UTC

    Keep in mind that because "lines" are of variable length, there is no way to simply jump to line 1000 without first sifting through the file line by line to determine where line 1000 begins in the file. So any solution you come up with (short of maintaining an index or requiring uniform-length lines) will require that the file be read at least once, at least until the proper line number is finally found.

    But you don't have to slurp the whole file into an array. It's fine to just read line by line, and take action when the desired line number is found.


    Dave

      actually this problem makes me think of a fairly recent discussion on p5p about the non-linear behaviour of the perl internal unicode implementation in the regex engine

      the problem is that to know the line position or the character position (supposing characters don't have all the same size) you have to count from the start...OR...do a bit of preparation, you put "markers" along the way

      maybe Tie::File uses something similar but essentially you keep the byte position of the start of every line = 0 modulo k, so to find line n *k +q seek to marker n and read q lines from there

      markers can be created as you fetch the lines, o pre-created in one go -- in the case of the regex engine, the algorithm could go both ways depending on the size of the "text" to match, or the maximum backtrack so far

      hth --stephan
Re: reading the file using the line number
by GrandFather (Saint) on Nov 29, 2006 at 08:13 UTC
Re: reading the file using the line number
by Fengor (Pilgrim) on Nov 29, 2006 at 09:18 UTC
    inspired by tom christiansens oneliners:

    perl -ne 'print if $. == $number; exit if $. > $number;'

    Edit: corrected typo, thx to davorg

    --
    "WHAT CAN THE HARVEST HOPE FOR IF NOT THE CARE OF THE REAPER MAN"
    -- Terry Pratchett, "Reaper Man"

      perl -ne 'print if $. = $number; exit if $. > $number;'

      You probably want '==', not '=', there.

      --
      <http://dave.org.uk>

      "The first rule of Perl club is you do not talk about Perl club."
      -- Chip Salzenberg

Re: reading the file using the line number
by tirwhan (Abbot) on Nov 29, 2006 at 07:43 UTC

    You coulld use Tie::File

    use Tie::File; use strict; use warnings; my @log_file_contents; tie @log_file_contents, 'Tie::File', "filename" or die "Can't open filename"; print "$log_file_contents[6]\n";
    This does not read the whole file into memory.

    All dogma is stupid.
Re: reading the file using the line number
by MonkE (Hermit) on Nov 29, 2006 at 13:36 UTC
    Tie::File seems to be the best solution proposed so far, but bear in mind that it doesn't magically know where line 6000 starts. If you open a file and then ask for line 6000, it will (internally) read from the beginning of file and count to line 6000. However, it does cache line offsets, so if you subsequently ask for line 6050, it will remember the byte offset for line 6000, jump straight to it, and then start counting lines until it reaches line 6050.

    If you're interested in learning more, have a look at the line number caching logic in File.pm (in the Tie::File package). It's groovy!

Re: reading the file using the line number
by padma (Initiate) on Dec 01, 2006 at 12:09 UTC
    You can use the following sub routine to extract particular lines from a file :
    sub extract { my ($filename, $line_no)=@_; my $line; open (FILE, $filename) || die "$filename can't be opened $! " if ($line_no =~ /\D/) { while ($line=<FILE>) { if ($line =~ /$line_no/) { return $line; } } } else { foreach (1..$line_no) { $line = <FILE>; } return $line; } }
    You have to pass the filename and the line number required. You can pass only one line no. required at a time. This can also be used to obtain a phrase from the file by passing the phrase to the sub routine.
    An example: $file = "example.txt"; $line_no_reqd = 4; $result = &imp($file,$line_no_reqd); print $result;
    This example will fetch the contents in line no. 4 from the file, example.txt and print it.
      sub extract { (my $filename, my $line_no)=@_; open (FILE, $filename) or die "$filename can't be opened: $! "; my $line = <FILE>; $. = $line_no; $line = <FILE> or die "Couldn't read line $line_no: $!"; return $line; }

      --
      "WHAT CAN THE HARVEST HOPE FOR IF NOT THE CARE OF THE REAPER MAN"
      -- Terry Pratchett, "Reaper Man"

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://586632]
Approved by GrandFather
Front-paged by smokemachine
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (9)
As of 2024-04-18 10:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found