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

Want to hava an array of items I want to find in a string.
Can use for loop but was wondering if Perl will iterate
through array without haveing to code loop.

Example:


@prims=("int","char","long","double","static");

Can this be done, doesn’t seem work this way

While(<>) {
    if(/@prims/) {
        print "Found item in :$_\n";
    }
}

Replies are listed 'Best First'.
Re: Pattern match array
by moritz (Cardinal) on May 07, 2008 at 15:12 UTC
    my $regex = join '|', @prims; while(<>){ print "Found item in $_" if m/$regex/; }

    If you wan the items in the array not to be interpreted as regexes, you have to use

    my $regex = join '|', map quotemeta, @prims;
Re: Pattern match array
by Fletch (Bishop) on May 07, 2008 at 15:17 UTC

    If you're using 5.10 you can use the new ~~ operator: if( $_ ~~ @prims ) { print "found item: $_\n"; }

    Update: Although as has been mentioned for some of the other solutions this is trying to do an eq match; if you're looking for a substring you probably want one of the join-y solutions.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: Pattern match array
by johngg (Canon) on May 07, 2008 at 16:14 UTC
    You probably want to use word boundary anchors so that you don't get false positives with fprintf when looking for int etc. Also you might have more than one of your @prims on one line so a global match could be in order.

    use strict; use warnings; my @prims = qw{ int char long double static }; my $rxPrims = do { local $" = q{|}; qr{\b(@prims)\b}; }; while ( <> ) { next unless my @found = m{$rxPrims}g; print qq{Found @found on line $.\n}; }

    I hope this is useful.

    Cheers,

    JohnGG

      You probably want to use word boundary anchors so that you don't get false positives with fprintf when looking for int etc.

      I agree. In other words, break the text into words before attempting to match entire words. So all the regex engine is doing is replicating an inner for-loop with an eq test.

      while ( <> ) { for my $word ( m{\b(\w+)\b}g ) { if (grep {$word eq $_} @prims) { print qq{Found $word on line $.\n}; } } }

      Update: which, of course, vindicates thezip's hash-based solution in the first response to this question.

        Letting the regex alternation do the heavy lifting seems to be a bit faster. Tested with a 1235 line C program cat'ed together 20 times.

        use strict; use warnings; use Benchmark q{cmpthese}; my @prims = qw{ int char long double static }; my $inFile = q{xxx.c}; open my $inFH, q{<}, $inFile or die qq{open: $inFile: $!\n}; my $outFile = q{/dev/null}; open my $outFH, q{>}, $outFile or die qq{open: $outFile: $!\n}; cmpthese( -10, { JohnGG => sub { seek $inFH, 0, 0; my $rxPrims = do { local $" = q{|}; qr{\b(@prims)\b}; }; while ( <$inFH> ) { next unless my @found = m{$rxPrims}g; print $outFH qq{Found @found on line $.\n}; } }, Narveson => sub { seek $inFH, 0, 0; while ( <$inFH> ) { for my $word ( m{\b(\w+)\b}g ) { if (grep {$word eq $_} @prims) { print $outFH qq{Found $word on line $.\n}; } } } }, } ); close $inFH or die qq{close: $inFile: $!\n}; close $outFH or die qq{close: $outFile: $!\n};

        The benchmark output.

        Rate Narveson JohnGG Narveson 1.39/s -- -63% JohnGG 3.78/s 173% --

        I hope this is of interest.

        Cheers,

        JohnGG

Re: Pattern match array
by thezip (Vicar) on May 07, 2008 at 15:09 UTC
    This would better be coded using a hash:
    use strict; use warnings; my %prims =( "int" => 1, "char" => 1, "long" => 1, "double" => 1, "static" => 1 ); while(<>) { if($prims{$_}) { print "Found item $_\n"; } }
    Update:
    If you absolutely need to have the @prim array, then create the hash on-the-fly using map:
    ... my @prim = (("int","char","long","double","static"); my %prim = map { $_ => 1 } @prim; ...
    Update2

    After reading moritz's take on this, his seems to be what you're looking for.

    I was somehow thinking that you were parsing chunks (ie. words) and then seeing if that word existed in your array/hash.

    moritz is also spot-on mentioning that you're not handling the newlines at the ends of lines.


    Your wish is my commandline.
      But it doesn't do the same thing. aennen searches for substrings, you search for exact matches.

      The fact that the lines aren't chomped means that you'll never get a match (unless on the last line, if it lacks a newline).

      Sorry should have expanded on input.
      Input is source code and I am trying to match
      the pattern in a string of data

      Example input date line

      int EB_Column::beginNPColumn(const bfpd_nc_t& i_rec, BF_COORDINATE_C_D(i_x))
Re: Pattern match array
by jettero (Monsignor) on May 07, 2008 at 15:30 UTC
    Maybe grep does what you want?
    $wanted = qr(something); if( grep { $_ =~ $wanted } @prims ) { print "hurray!\n" }

    I'm pretty sure you want the Fletch or the mortiz solution though.

    -Paul