Extract numeric + space / hyphen and alphanumeric values from text

Viewed 76

I am trying to extract numbers and unique identifiers from a string.

text = '+1 (123) 4567, 11111111, some text, 2222 2222, 333-33333-333, 444-444-444-4, some other text, 5555 5555 5555 5555 5, ab12345, xy7890z'

I have these regex:

regex_a = re.findall(r"\d+(?:[\s-]?\d+)*", text)
regex_b = re.findall(r"\(?\+[\d _\-\.\)\(\+]{8,25}[\d]{1}|\d{1,}", text)
regex_c = re.findall(r"^ (?=[a-zA-Z]+|\s+|\$)|(?:(?:[a-zA-Z]{0,}\s?\$?\s*)\d+(?:\.\d+)?)|(?:\d+(?:\.\d+)?\s?[a-zA-Z]{1,})$", text)
combined_regex = re.findall(r"\d+(?:[\s-]?\d+)*|\(?\+[\d _\-\.\)\(\+]{8,25}[\d]{1}|\d{1,}|^ (?=[a-zA-Z]+|\s+|\$)|(?:(?:[a-zA-Z]{0,}\s?\$?\s*)\d+(?:\.\d+)?)|(?:\d+(?:\.\d+)?\s?[a-zA-Z]{1,})$", text)

I intend to combine the 3 regexes into 1 regex. regex_c is used to extract the monetary amount of different combinations in the text. It is working for just that purpose. But I have an issue when combining the 3 regexes together.

Results from combined regex:

['+1 (123) 4567','11111111', '2222', '2222', '333','33333-333', '444,444-444-4', '5555', '5555', '5555', '5555', '5','ab12345','xy7890']

Results from regex_a:

['1', '123', '4567', '11111111', '2222 2222', '333-33333-333', '444-444-444-4', '5555 5555 5555 5555 5', '12345', '7890']

Results from regex_b:

['+1 (123) 4567', '11111111', '2222', '2222', '333', '33333', '333', '444', '444', '444', '4', '5555', '5555', '5555', '5555', '5', '12345', '7890']

Results from regex_c:

['1', '123', ' 4567', '11111111', ' 2222', ' 2222', ' 333', '33333', '333', ' 444', '444', '444', '4', ' 5555', ' 5555', ' 5555', ' 5555', ' 5', 'ab12345', 'xy7890']

The expected output:

['+1 (123) 4567','11111111', '2222 2222', '333-33333-333', '444-444-444-4', '5555 5555 5555 5555 5', 'ab12345', 'xy7890z']

Is there any way that I can combine the 3 regexes into 1 regex to extract the expected results and maintaining the logic for regex_c?

2 Answers

You may revamp your regex a bit and use in a re.finditer call:

(?<!\w)(?:(?:\+\d{1,4}[\s-]*)?(?:\(\d+\)|\d{2,})[-\s]*\d+(?=([\s-]?))(?:\1\d+)*|(?:[a-zA-Z]+\d|\d+[a-zA-Z])[a-zA-Z0-9]*)\b

See regex demo

Python code:

import re
text = '+1 (123) 4567, 11111111, some text, 2222 2222, 333-33333-333, 444-444-444-4, some other text, 5555 5555 5555 5555 5, ab12345, xy7890z'

rx = re.compile(r'''(?<!\w)(?: # Left-hand word boundary, start of a group:
  (?:\+\d{1,4}[\s-]*)?         # Optional: `+` and 1 to 4 digits and then 0+ whitespace/-
  (?:\(\d+\)|\d{2,})           # (, 1+ digits, ), or 2 or more digits
  [-\s]*                       # 0+ whitespaces or hyphens
  \d+                          # 1+ digits, and 
  (?=([\s-]?))(?:\1\d+)*       # 0 or more sequences of optional - or whitespace and 1+ digits
|                              # or
  (?:[a-zA-Z]+\d|\d+[a-zA-Z])  # 1+ letters and then a digit, or 1+ digits and then a letter
  [a-zA-Z0-9]*                 # 0 or more letters or digits
)\b                            # End of the group, word boundary''', re.VERBOSE)

print( [x.group() for x in rx.finditer(text)] )

Output:

[
    '+1 (123) 4567',
    '11111111',
    '2222 2222',
    '333-33333-333',
    '444-444-444-4',
    '5555 5555 5555 5555 5',
    'ab12345',
    'xy7890z'
]

Would you please try the following:

import re

text = '+1 (123) 4567, 11111111, some text, 2222 2222, 333-33333-333, 444-444-444-4, some other text, 5555 5555 5555 5555 5, ab12345, xy7890z'

print(re.sub(r'(?:^|(?<=,))\D*(?:,|$)', '', text))

Output:

+1 (123) 4567, 11111111, 2222 2222, 333-33333-333, 444-444-444-4, 5555 5555 5555 5555 5, ab12345, xy7890z

Explanation of the regex (?:^|(?<=,))\D*(?:,|$):

(?:^|(?<=,)) ... positive lookbehind which matches the beginning of the string or a leading comma
\D*          ... zero or more sequence of non-digit characters
(?:,|$)      ... a comma or the end of the string
Related