Ryan Dunphy
Ryan Dunphy

Reputation: 848

Perl Regex: Match this or that but NOT the other

Using Perl and Regex, is it possible to create a one-liner regex to match some phrases while NOT matching other? The rules are stored in a hash and the qualifier (column) needs to match the expression fed to it.

I have an object that contains qualifiers that I would prefer to feed in as a one-line regex. For example:

my %filters = {
  rule_a  => {
    regex  => {
      qualifier_a => qr/fruits/i,
      qualifier_b => qr/apples|oranges|!bananas/i, # NOT bananas
    }
  }
}

So, in this case, I want to match qualifier_a containing 'fruits' and qualifier_b containing 'apples', 'oranges', but NOT 'bananas'.

The script loops over each qualifier as follows:

my $match = 1; # Assume a match    
foreach my $qualifier (keys %filters{rule_a}{regex}) {
  # If fails to match, set match as false and break out of loop
  $match = ($qualifier =~ /%filters{rule_a}{regex}{$qualifier}/) ? 1 : 0);
  if(!$match){
   last;
  }
}
if($match){
  # Do all the things
}

Thoughts?

Upvotes: 3

Views: 1119

Answers (2)

RobEarl
RobEarl

Reputation: 7912

You can use a capture group and check the status of it:

$match = ($qualifier =~ /banana|(apples|oranges)/);
if (!($match && $1)) {
    last;
}

As banana isn't in the capture group, $1 won't be set if it matches.

See Regex Pattern to Match, Excluding when... / Except between for a much more detailed explanation of this technique.

Upvotes: 1

Lucas Trzesniewski
Lucas Trzesniewski

Reputation: 51390

Somewhat convoluted, but does its job:

^.*?bananas.*(*SKIP)(*FAIL)|apples|oranges

Demo

The first part will match the whole string if it contains bananas, and then force a failure without retrying at the next position.

The demo uses . to make lines independent, but you could use the s option if your string can contain newlines.

Upvotes: 3

Related Questions