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

Hello monks,

I've been looking for a way to make a regex either match or fail based on arbitrary tests of its backreferences. I've read perldoc perlre and I've got something working but it's fairly ugly and it seems that there should be a cleaner way to accomplish it.

In this simple example, I'm checking for percentages greater than 91:

$_ = "it's 94% fat-free!"; # a string that should match # the following code works but the logic is outside the RE print "match\n" if (/(\d{1,3})%/, $1>91); # the following code also works but is offensive print "match\n" if /(\d{1,3})%(?(?{$1>91})(?:.*)|(?!.*))/;
To comment the second RE above (I know about /x but there seems to be some strange interaction between it and ?( on my system):
/(\d{1,3})% # find a percentage to match (?( # begin conditional construct ?{$1>91}) # return value of a code block can be used to test (?:.*) # RE to use if true -- this always-true RE makes entire RE m +atch | (?!.*))/ # RE to use if false -- this always-false RE makes entire RE + fail
Any suggestions on doing this more elegantly?

Josh

Replies are listed 'Best First'.
Re: Conditionals within Regex
by sauoq (Abbot) on Aug 09, 2002 at 19:54 UTC
    Restructuring to allow the logic to reside outside the RE isn't a bad idea... but I know it's not what you want. That said, I don't think your RE is bad at all. (How offensive could it be, given what it does?)

    But you can clean it up a little:

    print "match\n" if /(\d{1,3})%(?(?{$1>91}).?|(?!.?))/;

    I think .? is probably better than .* and you don't need the (?: ) construct around your "yes-pattern" in the conditional.

    -sauoq
    "My two cents aren't worth a dime.";
    
      I suppose the only reason to have the logic inside the regex is the case addressed by the (?(condition)yes-pattern|no-pattern) construct, namely where there is additional matching to be done which is dependant on the outcome of the test. In my example there was no such dependency so as you noted it makes sense for the logic to be external to the regex.

      Thanks for helping me figure this out!

      Josh

Re: Conditionals within Regex
by blakem (Monsignor) on Sep 16, 2002 at 01:56 UTC
    print "match\n" if (/(\d{1,3})%/, $1>91);
    That code has a subtle bug in it. If the regex doesn't match, $1 will still be set to its previous value...
    #!/usr/bin/perl -w use strict; "abc 123" =~ /(\d+)/; $_ = "its a tub of lard"; print "'$_' matches\n" if (/(\d{1,3})%/, $1>91); __END__ 'its a tub of lard' matches
    Replacing the comma with && will fix it.
    if (/(\d{1,3})%/ && $1>91);
    In general, don't use $1 and friends without first checking that the regex you think they belong to actually matched.

    -Blake

      Thank you for pointing this out. You may have prevented several hands full of hair from being torn out at a future date :)

      Josh
Re: Conditionals within Regex
by japhy (Canon) on Sep 16, 2002 at 03:07 UTC
    You could always encode the logic directly into the pattern of matching strings: /(\d{3}|9[4-9])%/.

    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;