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

Greeting fellow monks.

I should start by saying I'm still very new to Perl, and that this is only homework in the sense that I am trying to remedy my ignorance. I want to know whether or not something is possible, and where I should go to read and learn more.

Here's my situation: I have an input file with some junk at the top of it, and then some lines I want to do something with. I know the last line of junk contains a certain pattern. What I wanted to do was tell Perl to start doing stuff after it read the line with that pattern in it. But I'm not sure how to do that within the while loop, or even if that's possible.

What I ended up doing, because I knew how, was to identify and skip the junk lines (all of which begin with either "qcp" or "#", or are blank) and then process all the other lines the way I wanted to, thus:

#!/usr/bin/perl use warnings; use strict; my ($vars); while <DATA>{ if (/^qcp|^\#|^\s?\n/){} else {do stuff here} } __DATA__ #data... qcp_junk qcp_more junk #cardtype... NOT_JUNK NOT_JUNK etc....

If I know that #cardtype Always begins the last line of junk, what could I do instead?

Thanks for any insight or wisdom you care to offer.



Replies are listed 'Best First'.
Re: Processing lines after a pattern in a while loop
by broquaint (Abbot) on Apr 05, 2004 at 16:36 UTC
    Looks like a job for the flip-flop operator (..) as seen in perlop e.g
    while(<DATA>) { chomp; next if /^(?:qcp|#)/ .. /^#cardtype/ or 0 == length; # do stuff here }


    update: added check for blank lines

Re: Processing lines after a pattern in a while loop
by kvale (Monsignor) on Apr 05, 2004 at 16:38 UTC
    Here is an easy way to skip the header:
    while(<DATA>){ last if /^#cardtype$/; } while(<DATA>){ # start processing here }


      And this is the solution that I would use. It is straightforward. It separates out loops which serve different purposes. It avoids the synthetic variable. What's not to like...?
      This is where I wish the magic while (<fh>) was a bit more inclusive, or a bit less special, or something.

      Too bad this doesn't work, because it sure makes for a readable idiom:

      <DATA> until /^#cardtype/; while (<DATA>) { ... }
      The closest readable alternative that works seems to be:
      1 until <DATA> =~ /^#cardtype/; while (<DATA>) { ... }

      [ e d @ h a l l e y . c c ]

Re: Processing lines after a pattern in a while loop
by ysth (Canon) on Apr 05, 2004 at 16:37 UTC
    .. in scalar context is a flipflop operator that does what you describe like this:
    while (<DATA>) { # skip everything from line 1 to the #cardtype line unless (1../^#cardtype$/) { # we're past the junk } }
    It works by returning true once the first condition is met (where the constant 1 here is implicitly compared to the current input line number) and continues to return true until the second condition is met. See perldoc perlop for more information.
Re: Processing lines after a pattern in a while loop
by Limbic~Region (Chancellor) on Apr 05, 2004 at 16:34 UTC
    This is fairly common task. Often what is desired is to only do something between two lines within a file. I often recommend using an off/on flag.
    my $flag = 0; while ( <INPUT> ) { chomp; if ( /pattern/ ) { $flag = 1; next; } next if ! $flag; # what you want to do goes here }
    Cheers - L~R
      Red flag alert!

      Do not name flags $flag. If you give flags such meaningless names, then one of the more common errors in your code will be that you accidentally reverse the meaning of a flag.

      Instead give flags names which are yes/no questions, which the flag's value is an answer to. Now you'll never have trouble keeping it straight, and nor will anyone else who glances at your code.

      If you think this advice is silly, then remind yourself of it the next time you catch a bug due to your accidentally reversing a flag's meaning...

      To echo what tilly said, don't use 'flag' for a variable name unless you're talking about a square or rectangle of cloth. Why not something more interesting like $seenJunkYet instead?

      I'd rather go for something like

      # Skip header junk while(<>) { last if /end of header junk/; } # Start processing real data while(<>) { # But quit if we see junk starting up again last if /start of trailer junk/; # Do useful stuff here }
      That way there aren't any flag variables. It's more lines of code, but it reads more easily.

      Alex / talexb / Toronto

      Life is short: get busy!