Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid

reading the file using the line number

by greatshots (Pilgrim)
on Nov 29, 2006 at 07:34 UTC ( #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 (Pope) 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 (Archbishop) 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.


      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 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 (Sage) 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

    -- Terry Pratchett, "Reaper Man"

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

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


      "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 (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; }

      -- Terry Pratchett, "Reaper Man"

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://586632]
Approved by GrandFather
Front-paged by smokemachine
[shmem]: discovered a fuel tank leakage at my second hand motorbike. Some bloke drove a screw too long through the fairing
[shmem]: hard to fix, and plans wrecked. And more... won't tell.
[shmem]: the leakage has been "fixed" with a piece of bycicle tube. Bleargh.
[karlgoethebier]: shmem: yes. my first ride tomorrow is questionable because of lumbago. shit
[shmem]: But! the bloke wasn't me, at least.

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (11)
As of 2017-06-25 20:00 GMT
Find Nodes?
    Voting Booth?
    How many monitors do you use while coding?

    Results (570 votes). Check out past polls.