Regex with one specific number and a word

Viewed 49

I would like to match a specific pattern with regex but I am running into catastrophic backtracking. I wonder if there's a way it would be possible to match what I would like and not get an error. I start with a simple assumption; I want my string to contain only one specific number e.g. 7 and only that specific number:

^\D*7\D*$

Only if I find this pattern do I want to look for another word in the same text such as "Coffee"; I put my condition into a group (^\D*7\D*$) and reference the group in my conditional and the then part will contain "Coffee":

(?(1)Coffee|)

Is there another phrasing that would avoid the the catastrophic backtracking?

2 Answers

You can use a negative lookahead to assert that the word Coffee is at the right.

^(?=.*\bCoffee\b)\D*7\D*$

The pattern matches:

  • ^ Start of string
  • (?= Positive lookahead, assert that on the right is
    • .*\bCoffee\b Match Coffee between word boundaries \b to prevent a partial match
  • ) Close lookahead
  • \D*7\D* Match number 7 between optional non digit characters.
  • $ End of string

Regex demo

Note that \D also matches a newline. If you don't want to cross newline boundaries, you can use [^\r\n\d] instead.

Left to right checking is more traditional:

^(?=.*Coffee)[^\d7]*7\D*$

See regex proof.

EXPLANATION

--------------------------------------------------------------------------------
  ^                        the beginning of the string
--------------------------------------------------------------------------------
  (?=                      look ahead to see if there is:
--------------------------------------------------------------------------------
    .*                       any character except \n (0 or more times
                             (matching the most amount possible))
--------------------------------------------------------------------------------
    Coffee                   'Coffee'
--------------------------------------------------------------------------------
  )                        end of look-ahead
--------------------------------------------------------------------------------
  [^\d7]*                  any character except: digits (0-9), '7' (0
                           or more times (matching the most amount possible))
--------------------------------------------------------------------------------
  7                        '7'
--------------------------------------------------------------------------------
  \D*                      non-digits (all but 0-9) (0 or more times
                           (matching the most amount possible))
--------------------------------------------------------------------------------
  $                        before an optional \n, and the end of the string

Right to left checking is only possible with engines like latest JavaScript, .NET or PyPi regex in Python:

^[^\d7]*7\D*$(?<=Coffee.*)

See proof.

EXPLANATION

--------------------------------------------------------------------------------
  ^                        the beginning of the string
--------------------------------------------------------------------------------
  [^\d7]*                  any character except: digits (0-9), '7' (0
                           or more times (matching the most amount
                           possible))
--------------------------------------------------------------------------------
  7                        '7'
--------------------------------------------------------------------------------
  \D*                      non-digits (all but 0-9) (0 or more times
                           (matching the most amount possible))
--------------------------------------------------------------------------------
  $                        before an optional \n, and the end of the
                           string
--------------------------------------------------------------------------------
  (?<=                     look behind to see if there is:
--------------------------------------------------------------------------------
    Coffee                   'Coffee'
--------------------------------------------------------------------------------
    .*                       any character except \n (0 or more times
                             (matching the most amount possible))
--------------------------------------------------------------------------------
  )                        end of look-behind
Related