http://qs321.pair.com?node_id=125027

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

Hello Monks. I am a humble seeker of your wisdom.

I need help writing a RegExp that matches when two words both are present in a string, but the order is unknown. Also, the words might or might not be next to eachother. The regexp should NOT match when only one word is present.

With nested if's it is trivial, but I would like to do it with a single regexp.

Example: If I have a file:

foo bar bar foo foo bar baz barbazfoo foofoobar

I wan't to match lines 1,2,6 and 7, but not 3 thorugh 5, using some match for foo and bar.

Any idea?

Edit Masem 2001-11-13 - Changed title from 'Regular expression and'

Replies are listed 'Best First'.
Re: Regular expression and
by davorg (Chancellor) on Nov 13, 2001 at 15:38 UTC

    Easiest solution is probably to use two regexes.

    while (<DATA>) { print if /foo/ && /bar/; } __DATA__ foo bar bar foo foo bar baz barbazfoo foofoobar
    --
    <http://www.dave.org.uk>

    "The first rule of Perl club is you don't talk about Perl club."

      or, if you don't know what the words are ahead of time:
      #!/usr/bin/perl -w use strict; my @approved = qw( foo bar); @approved = map { $_ = quotemeta $_; ### very important qr/$_/is; ## available in perl 6, not really needed } @approved; print "The approved list\n", join "\n", @approved; my $merex = join ' && ', map "/$_/", @approved; print "merex \n$merex\n\n"; if(@ARGV) { print "ARGV\n"; while(<DATA>) { print if eval $merex; } } else { while(<DATA>) { my $toprint = 0; for my $r(@approved) { $toprint++ if m/$r/; } print if $toprint == scalar @approved; } } __DATA__ foo bar bar foo foo bar baz barbazfoo foofoobar
      which yields:
      F:\dev>perl f.pl 3
      The approved list
      (?si-xm:foo)
      (?si-xm:bar)merex
      /(?si-xm:foo)/ && /(?si-xm:bar)/
      
      ARGV
      foo bar
      bar foo
      barbazfoo
      foofoobar
      
      F:\dev>perl f.pl
      The approved list
      (?si-xm:foo)
      (?si-xm:bar)merex
      /(?si-xm:foo)/ && /(?si-xm:bar)/
      
      foo bar
      bar foo
      barbazfoo
      foofoobar
      
      F:\dev>

      Chore List: 1 - buy more coffee 2 - re-impress upon davorg the importance of rule 1

       
      ___crazyinsomniac_______________________________________
      Disclaimer: Don't blame. It came from inside the void

      perl -e "$q=$_;map({chr unpack qq;H*;,$_}split(q;;,q*H*));print;$q/$q;"

      ISTR Camel (2) also indicated this is faster, I'd add word boundaries to davorg's regex too, if the words can also occur within other words ;)

      --

      Brother Frankus.

      ¤

Re: Regular expression and
by andye (Curate) on Nov 13, 2001 at 15:44 UTC
    #!/usr/bin/perl -w use strict; while (<DATA>) { chomp; print; print /(?=.*foo)(?=.*bar)/s ? "\t match \n" : "\t no match \n" +; } __DATA__ foo bar bar foo foo bar baz barbazfoo foofoobar
    gives output:
    foo bar match bar foo match foo no match bar no match baz no match barbazfoo match foofoobar match
    hth!

    andy.

    Update re jeroenes's points below:
    1. I used /s to make '.' match any character, in case fsn's *real* strings weren't all in one line.
    2. Yes, or even print grep /(?=.*foo)(?=.*bar)/, <DATA> ; , but I wanted to print the failed matches too. Perhaps print map{ chomp; /(?=.*foo)(?=.*bar)/?"$_ yes \n":"$_ no \n"} <DATA> ;

    ;)

      Two nitbits:
      1. You don't need the 's' modifier there, as the you already have the string in one line;
      2. This is a place to use grep as a filter: @matched  = grep /.../, <DATA>;

      Jeroen

      Wow! Exactly on the spot! Thankyou! (Getting an answer in 17 minutes. I'm amazed!)