289

With regex in Java, I want to write a regex that will match if and only if the pattern is not preceded by certain characters. For example:

String s = "foobar barbar beachbar crowbar bar ";

I want to match if bar is not preceded by foo. So the output would be:

barbar
beachbar
crowbar
bar
Andrew
  • 132
  • 1
  • 9
b3bop
  • 3,373
  • 2
  • 17
  • 17

3 Answers3

477

You want to use negative lookbehind like this:

\w*(?<!foo)bar

Where (?<!x) means "only if it doesn't have "x" before this point".

See Regular Expressions - Lookaround for more information.

Edit: added the \w* to capture the characters before (e.g. "beach").

Sebastián Palma
  • 32,692
  • 6
  • 40
  • 59
Adam Rofer
  • 6,121
  • 1
  • 14
  • 11
  • 3
    what modifications need to be made to not match foo_arbitrary_bar? ie foo not immediately preceding bar – Brad Kent Jun 28 '18 at 01:43
  • 2
    @BradKent `(?<!foo).*bar` would match something like that, I believe. – emyller Jul 17 '18 at 21:20
  • @emyller nope. that will match foobazbar (foo doesn't come before foobazbar, so it matches) – Brad Kent Jul 18 '18 at 21:53
  • 3
    @BradKent I didn't read your "not" in "not match", that why I said it would match. You case looks like a `\b(?!foo)\w*bar\b` to me then. – emyller Jul 19 '18 at 16:08
  • @emyller nope. that will match bazfoobar – Brad Kent Jul 19 '18 at 16:18
  • 4
    @BradKent Then you can either ask a new question, elaborating what you're trying to achieve in detail, or continue playing with regular expressions until you find it yourself. :) – emyller Jul 19 '18 at 18:30
  • What if I want to get all the numbers (or any other character/string) but the ones preceded by a given string: e.g.: `String s = "foo123 bar45 foo1 bar7"`. I'd like to get: 45 and 7. This re doesn't work properly: `(?<!foo)\d+` – Fernando S. Peregrino Apr 16 '19 at 10:34
  • Thanks for this. However this only match the first instance. How do I match all instances of bar not proceeded by foo? I tried \w*(?<!foo)barg but of course that is not right. Sorry I am quite new to Regex. – Freelensia May 23 '21 at 10:22
5

Another option is to first match optional word characters followed by bar, and when that has matched check what is directly to the left is not foobar.

The lookbehind assertion will run after matching bar first.

\w*bar(?<!foobar)
  • \w* Match 0+ word characters
  • bar Match literally
  • (?<!foobar) Negative lookbehind, assert from the current position foobar is not directly to the left.

Regex demo

The fourth bird
  • 154,723
  • 16
  • 55
  • 70
0

In some cases, it could be easier to optionally include the preceding part, then skip those matches in a second step. For instance, to find numbers that don't start with a "+":

if (preg_match_all('/(\+?[0-9][0-9\s\-].*[0-9])/s',$text,$matches)) {
    foreach($matches[1] as $match) {
        if(substr($match,0,1) == '+'){
            continue;
        }
        // continue processing
    }
}

The negative look behind did not work since it would still match 2+ digits, but it would not include the first digit in the match. For instance +1234 would be returned as 234.

Frank Forte
  • 2,031
  • 20
  • 19