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

I have a text file that contains user information that looks like:

John, Doe Michael
id:1234567890123 library:ACME
City, state:PUEBLO, CO
Phone:719-555-555
Street:1610 Sorrow AVE APT D
Zip: 81004

I am trying to write a simple perl script that looks for "id:" and then prints the 4 lines below the matching criteria. I have been having trouble accomplishing this, what would be the best way to accomplish this? Thanks for your help.

Replies are listed 'Best First'.
Re: Print 4 Lines below matching criteria
by cLive ;-) (Prior) on Nov 16, 2006 at 05:10 UTC
    m/ \nid: # match new line followed by id: [^\n]* # 0 or more characters that are not new lines \n # a newline ([^\n]*\n){4}) # the next 4 lines and store in $1 /sx; # treat as a '(s)ingle line' and (x) ignore whitesp +ace
    edit - I was sober too - omg, back to Idiocy for Dummies...

      Oops, you forgot to negate your character classes! Also, the s option is useless because . wasn't used.

      m/ \n id: # Match new line followed by id: [^\n]* # 0 or more characters that are not new lines \n # A newline ([^\n]*\n){4}) # The next 4 lines and store in $1 /x; # Ignore whitespace
Re: Print 4 Lines below matching criteria
by McDarren (Abbot) on Nov 16, 2006 at 06:34 UTC
    Update: my apologies - this does not actually work correctly - see replies below.

    For something a little more verbose:

    #!/usr/bin/perl -w use strict; while (<DATA>) { if (/^id:/) { my ($city, $phone, $street, $zip) = (<DATA>,<DATA>,<DATA>,<DAT +A>); print "$city$phone$street$zip"; } } __DATA__ John, Doe Michael id:1234567890123 library:ACME City, state:PUEBLO, CO Phone:719-555-555 Street:1610 Sorrow AVE APT D Zip: 81004

    Prints:

    City, state:PUEBLO, CO Phone:719-555-555 Street:1610 Sorrow AVE APT D Zip: 81004

    Cheers,
    Darren

      I didn't realize those readline calls would be in scalar context there. How interesting. Very nice!

      Update: I wrote a bad test case at nearly 11 pm. Sorry for the confusion!

        They are not in scalar context. To be in scalar context you would need to do:

        my ( $city, $phone, $street, $zip ) = ( scalar <DATA>, scalar <DATA>, +scalar <DATA>, scalar <DATA> );

      Darren - Thanks for your help, I used the script you suggested and it works great. DG
Re: Print 4 Lines below matching criteria
by GrandFather (Saint) on Nov 16, 2006 at 06:47 UTC

    If what you want are the lines fron "id:" to "Zip:" then:

    m/^id:/ .. m/^Zip:/ and print while (<INFILE>);

    does the trick.


    DWIM is Perl's answer to Gödel
Re: Print 4 Lines below matching criteria
by Anonymous Monk on Nov 16, 2006 at 09:44 UTC
    Never be afraid to reuse code! Simplest Perl script to do this:
    perl -e 'system "grep -A 3 ^id: $ARGV[0]"' file
    Or as a non-perl command:
    grep -A 3 ^id: file
    This might require you to install (GNU) grep, but luckely, that has ported to a wide range of platforms.
Re: Print 4 Lines below matching criteria
by jbert (Priest) on Nov 16, 2006 at 09:58 UTC
    This code does it in a fairly straightforward way (although I like the grep solution best).
    #!/usr/bin/perl use strict; use warnings; my $line; my $num_lines = 0; while ($line = <DATA>) { $num_lines = 4 if $line =~ /^id:/; if ($num_lines > 0) { print $line; $num_lines--; } } exit 0; __DATA__ John, Doe Michael id:1234567890123 library:ACME City, state:PUEBLO, CO Phone:719-555-555 Street:1610 Sorrow AVE APT D Zip: 81004 Jane, Doe Michael id:1234567890124 library:ACME City, state:PUEBLO, CO Phone:719-222-555 Street:1610 Happy AVE APT D Zip: 81006
    Andas the one-liner it started out as (much love for -n and $_ in the right context):
    perl -n -e '$n = 4 if /^id:/; if ($n) { print; $n--; }' your_data_file
Re: Print 4 Lines below matching criteria
by fenLisesi (Priest) on Nov 16, 2006 at 10:06 UTC
    The following might get you started. Cheers.
    use strict; use warnings; while (my $line = <DATA>) { if ($line =~ /^id:\d+/) { for (1 .. 4) { my $following = <DATA>; print $following; } } }

    Update: Sorry, jbert, your post was not on yet when I started.

      I tend to use scalar file reads when appropriate so as to avoid temporary variables so I would write your

      for (1 .. 4) { my $following = <DATA>; print $following; }

      like this

      print scalar <DATA> for 1 .. 4;

      It just looks a little cleaner to me.

      Cheers,

      JohnGG

      Small issue with your program: it will generate warnings if there's an 'id:' string in the last 4 lines of the file, as it will try to read past the end of the file.