Negative lookahead is ignored

Viewed 68

Consider the following regular expression:

https://regex101.com/r/svOSnY/1

I am trying to match out the memory amount and type form a Laptop HP Chromebook 14 G3 NVIDIA Tegra SOC 4GB DDRL 32GB FLASH 14inch 1366X768 Webcam Chrome OS, only, if it is not followed by an additional thing that looks like a memory amount. I thought that negative lookaheads are for exactly this reason:

(?!\d+\s?(gb|tb)) this is my negative lookahead

As it is applied now:

/(?:\d+)\s?(?:gb|tb)\s?(?:ddrl|ddr2)\s?(?!\d+\s?(gb|tb))/i

the 4gb ddrl part is still matched from my string, even though it is followed by the 32gb part which my negative lookahead should realise. If I change my negative lookahead, to a simple capture group, my regex correctly captures the whole 4gb ddrl 32gb part from the string.

What am I doing wrong?

2 Answers

Since you are declaring the space as optional, the regex egnine will try to match the string without considering the space; and indeed 4GB DDRL is not directly followed by 32GB FLASH (therefore it will be matched).

In order to fix it, put the optional space in your lookahead:

(?:\d+)\s?(?:gb|tb)\s?(?:ddrl|ddr2)(?!\s?\d+\s?(gb|tb))

See demo.

Backtracking is a keyword here.

When \s? matches a whitespace character, (?!\d+\s?(gb|tb)) does stumble upon 32GB and "rolls back" to the previous position, before that whitespace, and since it is a whitespace + 32GB, the lookahead closes the deal.

Use

\d+\s*[gt]b\s*ddr[l2](?!\s*\d+\s*[gt]b)

See regex proof. There is no chance the lookahead will re-match here because 1 and 2 have no quantifier.

EXPLANATION

--------------------------------------------------------------------------------
  \d+                      digits (0-9) (1 or more times (matching
                           the most amount possible))
--------------------------------------------------------------------------------
  \s*                      whitespace (\n, \r, \t, \f, and " ") (0 or
                           more times (matching the most amount
                           possible))
--------------------------------------------------------------------------------
  [gt]                     any character of: 'g', 't'
--------------------------------------------------------------------------------
  b                        'b'
--------------------------------------------------------------------------------
  \s*                      whitespace (\n, \r, \t, \f, and " ") (0 or
                           more times (matching the most amount
                           possible))
--------------------------------------------------------------------------------
  ddr                      'ddr'
--------------------------------------------------------------------------------
  [l2]                     any character of: 'l', '2'
--------------------------------------------------------------------------------
  (?!                      look ahead to see if there is not:
--------------------------------------------------------------------------------
    \s*                      whitespace (\n, \r, \t, \f, and " ") (0
                             or more times (matching the most amount
                             possible))
--------------------------------------------------------------------------------
    \d+                      digits (0-9) (1 or more times (matching
                             the most amount possible))
--------------------------------------------------------------------------------
    \s*                      whitespace (\n, \r, \t, \f, and " ") (0
                             or more times (matching the most amount
                             possible))
--------------------------------------------------------------------------------
    [gt]                     any character of: 'g', 't'
--------------------------------------------------------------------------------
    b                        'b'
--------------------------------------------------------------------------------
  )                        end of look-ahead
Related