Expressing AND, OR, and NOT in a Single Pattern

Problem

You have an existing program that accepts a pattern as an argument or input. It doesn’t allow you to add extra logic, like case insensitive options, ANDs, or NOTs. So you need to write a single pattern that matches either of two different patterns (the “or” case), both of two patterns (the “and” case), or that reverses the sense of the match (“not”).

This situation arises often in a configuration files, web forms, or command-line arguments. Imagine there’s a program that does this:

chomp($pattern = <CONFIG_FH>);
if ( $data =~ /$pattern/ ) { ..... }

As the one maintaining the contents of CONFIG_FH, you need to convey Booleans through to the matching program through that one, measly pattern without explicit connectives.

Solution

True if either /ALPHA/ or /BETA/ matches, like /ALPHA/ || /BETA/:

/ALPHA|BETA/

True if both /ALPHA/ and /BETA/ match, but may overlap, meaning "BETALPHA" should be okay, like /ALPHA/ && /BETA/:

/^(?=.*ALPHA)(?=.*BETA)/s

True if both /ALPHA/ and /BETA/ match, but may not overlap, meaning that "BETALPHA" should fail:

/ALPHA.*BETA|BETA.*ALPHA/s

True if pattern /PAT/ does not match, like $var !~ /PAT/:

/^(?:(?!PAT).)*$/s

True if pattern BAD does not match, but pattern GOOD does:

/(?=^(?:(?!BAD).)*$)GOOD/s

Discussion

When you’re writing a regular program and want to know if something doesn’t match, say one of:

if (!($string =~ /pattern/)) { something() } # ugly if ( $string !~ /pattern/) { something() } # preferred ...

Get Perl Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.