Regex match if there is only one match from a number of choices in a string

Viewed 413

Is there a pattern for finding if there is only one match from a number of options in a string?

Let's say we have the following list of potential matches:

`foo|bar|baz`

And we have the strings:

`Foo bar, Cambaz!` // shoud not match due to multiple findings
`My bar, good!` // shoud match for `bar`
`My friend, Cambaz!` // shoud match for baz

Case insensitive.

3 Answers

Use

(?i)^(?:(?!foo|bar|baz).)*\K(?:foo|bar|baz)(?!.*(?:foo|bar|baz))

See proof.

Match some text that does not match any of the alternatives up to the first match (^(?:(?!foo|bar|baz).)*), omit the matched text (\K), match the word from list ((?:foo|bar|baz)), and then check there is no word from the list later in the text ((?!.*(?:foo|bar|baz))).

(?i) - case insensitive.

Shortened version with subroutines:

(?i)^(?:(?!(foo|bar|baz)).)*\K\g<1>(?!.*\g<1>)

See another proof

\g<1> stands for the (foo|bar|baz) pattern.

You may use this PCRE regex:

^(?>.*?(foo|bar|baz)){2}.*(*SKIP)(*F)|(?1)

RegEx Demo

  • (*F) or (*FAIL) verb behaves like a failing negative assertion and is a synonym for (?!)
  • (*SKIP) defines a point beyond which the regex engine is not allowed to backtrack when the subpattern fails later
  • The idea of the (*SKIP)(*FAIL) trick is to consume characters that you want to avoid, and that must not be a part of the match result.
  • (?1): recurses the 1st subpattern

You can also use (*COMMIT) to abort further researches when the subpattern after (a lookahead here) fails:

(foo|bar|baz)(*COMMIT)(?!.*(?1))

demo

Related