Regex failing to exclude matches with line break

Viewed 99

I run the following regex. I am breaking it down using variables for greater clarity:

all_no_numb_newline = r'(?:[^\n\d]*\n)' ## I include an extra line just to get more context ##
all_no_numb = r'(?:[^\n\d]*)' ## I do not want there to be any numbers on the same line except the ID ##
x1 = r'(?!(1-888-555|\(888\)))' ## I am excluding a specific common phone number ##
x2 = r'(?![\n\/])\W{0,2}' ## I am excluding line breaks and date formats ##
id_re = f'({x1}\d(?:{x2}\d){{16}}\d)' ## This is an ID number 18 digits long with some symbols in between ##

Basically, I am trying to identify an ID that is 18 digits long. I don't want to match 18 digits that include any letters, line breaks, or forward slashes. If I match an 18 digit ID with other random symbols, that is fine. I also don't want to match to an ID preceded by any numbers. I also want to match to an extra line before the main group to give me better context about my match, however I am really after the id_re match (this is why I put a question mark "?" next to the all_nno_numb_new_line).

Then I run the following code using:

re.findall(
    "("+
    all_no_numb_newline+"?"+
    all_no_numb+
    id_re+")"
    , text)[0]

However, this is still returning the following match:

('L1 (061510)\n1009671-1000', '1 (061510)\n1009671-1000', '')

I expected there to be no line breaks and I expected there to be two groups (my general match and my ID group). Why are there 3 groups instead of 2? And why does "\n", ie a line break, appear in the match?

EDIT: EXAMPLES OF MATCHES

'Mortgage\nID 756953480812037780'
')\n*DT756953480812037780'
'\nq75695348081 0233 240'
')\n*DT756953480812037780'
'\nq03313375233 0233 329'
'ID 676170114397739293'
'ID NUMBER 676170114397739293'
'ID\n676170114397739293'
'ID676170114397739293'

OUTPUT:

'756953480812037780'
'756953480812037780'
'75695348081 0233 240'
'756953480812037780'
'03313375233 0233 329'
'676170114397739293'
'676170114397739293'
'676170114397739293'
'676170114397739293'

EDIT: EXAMPLES THAT SHOULD NOT MATCH

'L1 (061510)\n1009671-1000'
'L1 081510)\n1009671-1000'
'L1 (061510)\n1009671-1000'
2 Answers

Use

(?<!\d)\d(?:\s*\d){16}\d(?!\d)

See proof.

Explanation

--------------------------------------------------------------------------------
  (?<!                     look behind to see if there is not:
--------------------------------------------------------------------------------
    \d                       digits (0-9)
--------------------------------------------------------------------------------
  )                        end of look-behind
--------------------------------------------------------------------------------
  \d                       digits (0-9)
--------------------------------------------------------------------------------
  (?:                      group, but do not capture (16 times):
--------------------------------------------------------------------------------
    \s*                      whitespace (\n, \r, \t, \f, and " ") (0
                             or more times (matching the most amount
                             possible))
--------------------------------------------------------------------------------
    \d                       digits (0-9)
--------------------------------------------------------------------------------
  ){16}                    end of grouping
--------------------------------------------------------------------------------
  \d                       digits (0-9)
--------------------------------------------------------------------------------
  (?!                      look ahead to see if there is not:
--------------------------------------------------------------------------------
    \d                       digits (0-9)
--------------------------------------------------------------------------------
  )                        end of look-ahead

I don't think Ryszard's answer eliminated \n. I went about it a more hackey way:

                YY = r'(?!-888-)'
                XX = r'[^A-Za-z\d\n\\\/\)\(]{0,2}'
                id_re= f'({YY}\d(?:{XX}\d|\d{XX}){{16}}\d)'

YY eliminates a common phone number from showing up XX keep all non-alphanumeric characters excluding \n. Other procedures kept showing up with \n no matter how many lookaheads/behinds I implemented. So I decided to using a simpler but more hack of a route by hand-eliminating all alphanumeric and \n (along with extra symbols that would confuse dates or phone numbers with slashes and parentheses).

This regex was so successful that I got almost a 99% match!

Related