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

Wise Monks,

In a command line telephonebook search program, people can use any number of arguments which must all match.
Example:
TelSearch.pl bill clinton
Searches for all entries that have both 'bill' AND 'clinton' in the entrystring (which contains name, alias, room# etc)

Is it possible to make ONE regexp for this purpose? It would be easy if the order of the arguments mattered, but it doesn't. Example:
TelSearch.pl bill clinton
Needs to find the same entries as
TelSearch.pl clinton bill
I currently use something like this:
my $flag = 1; foreach (@ARGV) { if ($entry !~ m/\Q$_\E/) { $flag = 0; } }

Replies are listed 'Best First'.
Re: regexp match arg1 AND arg2
by broquaint (Abbot) on Feb 17, 2003 at 15:19 UTC
      Thank you. Although not one regexp, the other topic had a shorter way to do it:
      doSomething() if grep $data =~ /\Q$_/, @vals;
      That should match all in no particular order.
Re: regexp match arg1 AND arg2
by Limbic~Region (Chancellor) on Feb 17, 2003 at 15:33 UTC
    There is definately MTOWTDT.

    Since you have only requested assistance for two arguments, you might try something like:

    if ($entry =~ /\Q$ARGV[0]\E/ && $entry =~ /\Q$ARGV[1]\E/) { # Do something }

    The problem is that doesn't technically meet your requirements of ONE regexp.

    This would be another way to do it with ONE regexp:

    if ($entry =~ /(\Q$ARGV[0]\E.*\Q$ARGV[1]\E|\Q$ARGV[1]\E.*\Q$ARGV[0]\E) +/ { # Do something }

    But that is going to be much slower and probably has about a dozen ways in which it won't work

    My recommendation would be to standardize your data fields (i.e. first name, last name, etc) and then to use command line options to match them.

    TelSearch.pl -f bill -l clinton
    or
    TelSearch.pl -l clinton -f bill

    Hope this helps!

    Cheers - L~R

    Update: Corrected regexp that Enlil pointed out would only match one of the two arguments.

      No i am sorry but people can use any number of arguments that must match. So your solution will not work :(
        Then I suggest you try one of the suggestions that broquaint already pointed out, or you take my advice and structure the data and provide command line options.

        Cheers - Limbic~Region

Re: regexp match arg1 AND arg2
by hv (Prior) on Feb 17, 2003 at 17:36 UTC

    If you particularly want a single regular expression, you can get it by using lookahead:

    my $re = join ' ', map "(?= .*? \Q$_\E)", @ARGV; if ($entry =~ /^ $re/sx) { ... }

    That would turn your example arguments into something like:

    m{ ^ (?= .*? bill ) (?= .*? clinton ) }xs
    so it matches if:
    from the start fail unless we can find some characters followed by "bill" _and_ (still from the start) fail unless we can find some characters followed by "clinton" succeed

    I'm not sure how efficient that is, but it should be ok; if speed is important it would be worth benchmarking against some alternatives. Don't forget that when looking for an exact string match, index() can be faster than m/\Q$str\E/, and that if you're going to match one string against many regexps study($string) can help.

    Hugo
      Wow this is exactly what i meant. Thank you! It works like a charm.
Re: regexp match arg1 AND arg2
by zengargoyle (Deacon) on Feb 17, 2003 at 17:58 UTC

    something like this perhaps...

    @matches = map {qr/\Q$_\E/} @ARGV; while ($rec = <DATA>) { print $rec if @matches # we have something to look for and ( grep {$rec =~ $_} @matches # the number of matches found ) == @matches; # equals the number specified } __END__ Bill Gates Bill Bonko John Random Quincy Gates
    $ perl test.pl Bill
    Bill Gates
    Bill Bonko
    $ perl test.pl Bill Bonko
    Bill Bonko
    $ perl test.pl Bonko     
    Bill Bonko
    perl test.pl Gates
    Bill Gates
    Quincy Gates
    $ perl test.pl Gates Bill
    Bill Gates