Welcome to Abdul Malik Ikhsan's Blog

Practical Regex 3: Using Nested Negative Lookahead

Posted in regex, tips and tricks by samsonasik on February 18, 2022

So, you need to verify a statement, that in the statement, something has NOT followed with other thing, except continue with another thing, for example:

I like foo                 << - not allowed
I like fooSomeRandomString << - not allowed
I like food                << - allowed
I like football            << - allowed
I like drawing             << - allowed
I dislike drawing          << - not allowed

First, let’s create a regex to make “I like” not followed with “foo” with Negative Lookahead “(?!foo)”:

I like (?!foo)[^ ]+

Above regex will match only for “I like drawing”, ref https://regex101.com/r/hAsvYH/1,

Important note:

I used caret for negation character class [^ ] for not space just to ensure it exactly has word found next with non space after collection of character so we can see the result of next word.

Next, continue with another requirement, except “food” and “football”, so we make another Negative Lookahead inside Negative Lookahead “(?!foo(?!d|tball))”:

I like (?!foo(?!d|tball))[^ ]+

Above regex will verify next followed with “foo” is allowed if next of “foo” is “d” or “tball”, so it will matches:

I like food
I like football
I like drawing

ref https://regex101.com/r/hAsvYH/2

Next, how to get “food”, “footbal”, and “drawing”? You can use “Named Capture Group” that cover “Nested Negative Lookahead + “character class [^ ]+” so the word next will be included as a “Named Capture Group”, like the following:

I like (?<like>(?!foo(?!d|tball))[^ ]+)

Above, the word result is captured with named “like”, ref https://regex101.com/r/hAsvYH/3

Let’s use real code to get it, for example, with PHP:

$text = <<<TEXT
I like foo                 << - not allowed
I like fooSomeRandomString << - not allowed
I like food                << - allowed
I like football            << - allowed
I like drawing             << - allowed
I dislike drawing          << - not allowed
TEXT;

$pattern = '#I like (?<like>(?!foo(?!d|tball))[^ ]+)#';
preg_match_all($pattern, $text, $matches);

foreach($matches['like'] as $like) {
    echo $like . PHP_EOL;
}

Ref https://3v4l.org/R7TfC

That’s it 😉

References: