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

Is it pssoble to have a match expression only match a specific line of text in a text file. There are multiple lines in the text file that will match the expression, however each line is different and special. For example, I would like to match items in lines 1 and 2, but those numbers will change for each file I am using, but not the line they are on.

1 12345.67890 1 13456.12345

I would need to match the 12345.67890 from line 1 and the 13456.12345 from line 2, but those numbers could just as easily be...

1 54321.54321 1 34567.56789
...in another file. Thanks for any help

Update: Thanks a bunch, those ideas got me in the right direction!

20060508 Janitored by Corion: Restored original content

Replies are listed 'Best First'.
Re: Matching on a specific line of text
by bart (Canon) on May 07, 2006 at 09:55 UTC
    Yes it's possible. The special variable $. can be of great help, it keeps track of the line number of the filehandle you last read from. So you can do:
    open IN, "<", $file or die "Can't open file $file: $!"; my %data; while(<IN>) { if($. == 1 || $. == 2) { my($number) = /([\d.]+)\s*$/; $data{$.} = $number; } }
    If it doesn't match, it fills in undef for the data.

    It's a bit silly for such small line numbers, but for larger numbers this approach begins to make sense.

    I use a hash as a sparse array, it keeps a connection between the line number and the read data without wasting space for inbetween line numbers. Again, this makes most sense if your line numbers are much bigger than 1 or 2.

    (update fixed error in code, thanks to davidrw for pointing that out.)

      I forgot about the special behaviour of .. in scalar context, when the operands are literal integers. In that case, that value is automatically compared against $.. So my code can be simplified to:
      open IN, "<", $file or die "Can't open file $file: $!"; my %data; while(<IN>) { if(1 .. 2) { my($number) = /([\d.]+)\s*$/; $data{$.} = $number; } }

      If you need several distinct ranges, you can do

      if(1 .. 2 or 6 .. 8) {
      as can be demonstrated using the following code:
      while(<DATA>) { if(1 .. 2 or 6 .. 8) { print "$.: $_"; } } __DATA__ one two three four five six seven eight nine ten
      Result:
      1: one
      2: two
      6: six
      7: seven
      8: eight
      
      For a single value, you can choose between 4 == $. and 4 .. 4:
      if(1 .. 2 or 4 == $. or 6 .. 8) {
      if(1 .. 2 or 4 .. 4 or 6 .. 8) {
      can add a else{ last } to the if statement so it doesn't bother reading the rest of the file.. Also, if the desired lines are at the beginning, can just do:
      my %data; ($data{$_}) = <IN> =~ m/(\d+\.\d+)\s*$/ for 1 .. 2;
      (note it's <IN>, not <$file>)
        add a else{ last } to the if statement so it doesn't bother reading the rest of the file.
        Careful if there are holes in your test range... you might jump out of the loop too early. In that case, it's best to do an explicit compare against the maximum line number you're interested in.
        elsif($. > $lastline) { last; }
Re: Matching on a specific line of text
by Samy_rio (Vicar) on May 07, 2006 at 10:09 UTC

    Hi kingdom, If I understood your question correctly then workout this,

    use strict; use warnings; use Switch; open (FILE, shift)||die $!; while(<FILE>){ switch ($.) { case 1 { (m/^12345.67890$/) ? (print "Line $. \"$&\" Matched\n") +: (print "Line $. Not Matched\n"); } case 2 { (m/^13456.12345$/) ? (print "Line $. \"$&\" Matched\n") +: (print "Line $. Not Matched\n"); } case [3..5]{ (m/^\d+$/) ? (print "Line $. \"$&\" Matched\n") : (p +rint "Line $. Not Matched\n"); } } } close(FILE);
    Input is: (test.txt) 12345.67890 13456.12345 12A345.67890 13E456.12345 12B345.67890 Output is: Line 1 "12345.67890" Matched Line 2 "13456.12345" Matched Line 3 Not Matched Line 4 Not Matched Line 5 Not Matched

    Regards,
    Velusamy R.


    eval"print uc\"\\c$_\""for split'','j)@,/6%@0%2,`e@3!-9v2)/@|6%,53!-9@2~j';

      OP wants to match on a pattern, e.g. \d+\.\d+, not the specific number 12345.67890 .. (note also in your regex you didn't escape the period).

      a switch is probably overkill here -- can just use if/else .. and avoids the caveats about using Switch -- see this node: using switch