Regex match no groups, 1st group OR 2nd group but not both. Something like 'NAND'

Viewed 83

I can't figure out how to write regex that matches these:

  • everyone hi
  • hi everyone
  • hi

But not this:

  • everyone hi everyone

The regex (?:everyone )?hi(?: everyone)? will match the latter as well (which is not what I want). How to make such a regex? Or is it just not possible? I couldn't do enough research because I couldn't express the problem in correct words. Sorry if I posted a duplicate

3 Answers

Here is a brute force alternation way to get this done:

^(?:everyone +hi|hi(?: +everyone)?)$

RegEx Demo

RegEx Details:

  • ^: Start
  • (?:: Start a non-capture group
    • everyone: Match everyone:
    • +hi: Match 1+ spaces followed by hi
    • |: OR
    • hi: Match hi:
  • (?: +everyone)?: Optionally match 1+ spaces followed by everyone
  • ): End non-capture group
  • $: End

You could explicitly make a regex for each case (the first will capture two), utilizing beginning and end of line tokens

(^hi( everyone)?$) (^everyone hi$)

If you need to match these texts in a larger text, you can use

\b(?:everyone hi(?! everyone)|(?<!everyone )hi(?: everyone)?)\b

See the regex demo.

Details

  • \b - a word boundary
  • (?: - start of a non-capturing group:
    • everyone hi(?! everyone) - everyone hi not followed by a space and everyone
    • | - or
    • (?<!everyone )hi(?: everyone)? - hi (not immediately preceded with everyone and a space) and an optional space+everyone
  • ) - end of the non-capturing group
  • \b - a word boundary

With PyPi regex, you can have a more robust regex:

\beveryone hi everyone\b(*SKIP)(*F)|\b(?:(?:everyone\s+)?hi(?:\s+everyone)?)\b

See this regex demo and this Python demo:

import regex
text = "everyone hi\nhi everyone\nhi\nBut not this:everyone hi everyone"
rx = r"\beveryone hi everyone\b(*SKIP)(*F)|\b(?:(?:everyone\s+)?hi(?:\s+everyone)?)\b"
print( regex.findall(rx, text) )
## => ['everyone hi', 'hi everyone', 'hi']
Related