http://www.perlmonks.org?node_id=204265

P0w3rK!d has asked for the wisdom of the Perl Monks concerning the following question:

After finding "[foo]", how do you print out everything after it until you reach [foo2]? How do you get to the next element within @lines inside of the foreach? I only want to read the file 1 time..not n times.
foreach my $line (@lines) { if ($line =~ m/\[foo\]/) { print $line; # I do not want this line # how do I print the next lines in @lines # until I reach [foo2]? # once I get to [foo2] I want to print out # everything there also } }

File:

; ; foo file ; [foo] a=1 b=2 c=3 [foo2] d=4 e=5 f=6

Replies are listed 'Best First'.
Re: How do you move within an array using foreach?
by Ovid (Cardinal) on Oct 10, 2002 at 18:34 UTC

    I'm a bit confused by your question as what you state in the question seems, to me, to not match the code comments. Perhaps if you were to provide sample output we might better understand. In the meantime, I am taking your question to mean "given start and end tokens in a file, how do I print out all lines from the start and end tokens?".

    #!/usr/bin/perl -w use strict; my $start = qr/\Q[foo]\E/; my $end = qr/\Q[foo2]\E/; while ( <DATA> ) { if ( /$start/ .. /$end/ ) { print; } } __DATA__ ; ; foo file ; [foo] a=1 b=2 c=3 [foo2] d=4 e=5 f=6

    Cheers,
    Ovid

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

      Ovid's solution works but keeps scanning the file even after [foo2] is found. It is no big deal if the foo file is small but if it is huge it might take a while. I added a flag to bail out once [foo2] is found.
      #!/usr/bin/perl -w use strict; my $start = qr/\Q[foo]\E/; my $end = qr/\Q[foo2]\E/; my $flag = 0; while ( <DATA> ) { if (/$start/ .. /$end/) { $flag++; print; next; } last if $flag; } print "$flag lines printed\n"; __DATA__ ; ; foo file ; [foo] a=1 b=2 c=3 [foo2] d=4 e=5 f=6

      --

      flounder

      That's how I read it also. If that's what the questioner is after, I have to do it often enough that I usually write it as a one-liner: perl -ne "print if /\[foo\]/../\[foo2\]/" foofile(Nothing wrong with your code, just wanted to point out a short-circuit version.)

      BTW, I used double-quotes in that example because I presume the questioner is doing this on a windows system; i.e. typing at cmd.exe or something.

      Ovid,
      There are actually n number of tokens in the file. I was trying to simplify the question. :)

      As I read each section, I need to drop each of these into a hash.
      For example:

      # there are n number of files to parse %masterhash = ( %file_1_hash, ..., %file_n_hash); # within each file hash %file_1_hash = (%foo, %foo2); #within each section %foo = ( letter => "a", number => "b" ); # %letter would = a # %number would = b ... with n number of elements within foohash # same for foo2 %foo2 = ( %letter, %number); # same as foo #... Note: n number of sections in each file.
      Does that make sense?

      -P0w3rK!d :)

        By "section", are you referring to .ini file sections? If so, do you know about Config::IniFiles? I haven't used it, but it's a tied-hash interface that looks like it does what you want.

        If you are dealing with .ini files, but this isn't exactly the right thing, poke around CPAN; there's a lot of modules for dealing with them, so you can probably find one that does whatever you need.

        # please forgive me..there are the hashes.. %foo1 = ( a => "1", b => "2", c => "3", ); %foo2 = ( d => "4", e => "5", f => "6", );
Re: How do you move within an array using foreach?
by JaWi (Hermit) on Oct 10, 2002 at 18:31 UTC
    While not tested, I saw this elegant solution in this node:
    print if /\[foo\]/ .. /\[foo2\]/ foreach ( @lines );
    Credits go to zigdon... ;-)

    Update: Check Ovid's answer; it's more strict in the requirements set.

    -- JaWi

    "A chicken is an egg's way of producing more eggs."

      That looks like a useful thing to have around - I've never seen the /foo/ .. /bar/ notation before - I don't suppose you could explain it, or point me to a place that does?
        It's called the "flip-flop" operator. See perlop
        Look here in perlop.

        In scalar context, ".." returns a boolean value. The operator is bistable, like a flip-flop, and emulates the line-range (comma) operator of sed, awk, and various editors. Each ".." operator maintains its own boolean state. It is false as long as its left operand is false. Once the left operand is true, the range operator stays true until the right operand is true, AFTER which the range operator becomes false again.

        --

        flounder

        As a matter of fact, I can (since about 15 minutes :-)! Summarizing from perlop:

        The two dots work like a sort of flip-flop: if returns true if the first operation (the /foo/ part) evaluates `true' and stays true until the second operation evaluates to `true' (the /bar/ part). After that it returns false again.

        Cheerio,

        -- JaWi

        "A chicken is an egg's way of producing more eggs."

        Look in the perlop documentation, under the "Range Operators" section. All the various uses of .. are explained there in detail.

        -- Mike

        --
        just,my${.02}

Re: How do you move within an array using foreach?
by strider corinth (Friar) on Oct 10, 2002 at 18:32 UTC
    I'd probably just use a flag variable:

    my $flag = 0; foreach my $line( @lines ){ $flag = 1 if $line =~ m/\[foo\]/; $flag = 0 if $line =~ m/\[foo2\]/; print $line if $flag; }

    --

    Love justice; desire mercy.
Re: How do you move within an array using foreach?
by fruiture (Curate) on Oct 10, 2002 at 18:36 UTC

    1. Use a flag. 2. If you read a file as you mentioned, do not use arrays at all:

    open my $fh,'<', $filename or die $!; my $flag; while( <$fh> ){ unless( $flag ){ next unless /^start_mark/; $flag = 1; } elsif( /^end_mark/ ){ $flag = 0; } } continue { print if $flag } close $fh;
    --
    http://fruiture.de
Re: How do you move within an array using foreach?
by rir (Vicar) on Oct 10, 2002 at 18:58 UTC
    I'm not sure whether you want your foo line or not.
    But this will do for small sets.
    #!/usr/bin/perl use strict; use warnings; foreach (<DATA>) { if ( /begin/ .. /end/) { if ( /begin/ ) { print "You want to skip me, yes? $_"; next; } print; next; } } __END__ 1 2 3 4 begin 6 7 end 9
Re: How do you move within an array using foreach?
by jj808 (Hermit) on Oct 10, 2002 at 18:41 UTC
    I read the question as 'print everything after [foo] until you get to [foo2] - don't print [foo2] itself but print everything that comes after it'.
    cat foofile | perl -ne 'if (/\[foo\]/){$print++;next};next if /\[foo2\ +]/;print if ($print)'
    JJ