6

I'm wondering what is the proper way to test if a named capture group exists. Specifically, I have a function that takes a compiled regex as an argument. The regex may or may not have a specific named group, and the named group may or may not be present in a string being passed in:

some_regex = re.compile("^foo(?P<idx>[0-9]*)?$")
other_regex = re.compile("^bar$")

def some_func(regex, string):
    m = regex.match(regex, string)
    if m.group("idx"):     # get *** IndexError: no such group here...
        print(f"index found and is {m.group('idx')}")
    print(f"no index found")

some_func(other_regex, "bar")

I'd like to test if the group exists without using try -- as this would short circuit the rest of the function, which I would still need to run if the named group was not found.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
HardcoreHenry
  • 5,909
  • 2
  • 19
  • 44
  • 1
    why would `try/except` "short circuit the rest of the function"? – timgeb Mar 01 '22 at 13:27
  • It is here [How to get group name of match regular expression in Python?](https://stackoverflow.com/questions/28856238/how-to-get-group-name-of-match-regular-expression-in-python) – The fourth bird Mar 01 '22 at 13:43
  • I suppose I could do a local `try`/`except` -- sorry new to python, so I always imagine a `try` around a whole block of code rather than around one or two lines... – HardcoreHenry Mar 01 '22 at 13:57
  • I added both scenarios where a named capturing group is checked for to my answer. – Wiktor Stribiżew Mar 02 '22 at 19:40

2 Answers2

9

If you want to check if a match data object contains a named group capture, i.e. if a named group matched, you can use the MatchData#groupdict() property:

import re
some_regex = re.compile("^foo(?P<idx>[0-9]*)?$")

match = some_regex.match('foo11')
print(match and 'idx' in match.groupdict()) # => True

match = some_regex.match('bar11')
print(match and 'idx' in match.groupdict()) # => None (coerceable to False)

See the Python demo. Note that if you need a boolean output, simply wrap the expression inside print with bool(...): print(bool(match and 'idx' in match.groupdict())).

If you need to check if a group with a specific name exists in the compile pattern, you can use Pattern.groupindex to check if the group name exists:

def some_func(regex, group_name):
   return group_name in regex.groupindex

The documentation says:

Pattern.groupindex
A dictionary mapping any symbolic group names defined by (?P<id>) to group numbers. The dictionary is empty if no symbolic groups were used in the pattern.

See the Python demo:

import re
some_regex = re.compile("^foo(?P<idx>[0-9]*)?$")
other_regex = re.compile("^bar$")

def some_func(regex, group_name):
   return group_name in regex.groupindex

print(some_func(some_regex,"bar"))  # => False
print(some_func(some_regex,"idx"))  # => True
print(some_func(other_regex,"bar")) # => False
print(some_func(other_regex,"idx")) # => False
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
1

You can check the groupdict of the match object:

import re
some_regex = re.compile("^foo(?P<idx>[0-9]*)?$")

match = some_regex.match('foo11')
print(True) if match and 'idx' in match.groupdict() else print(False) # True
match = some_regex.match('bar11')
print(True) if match and 'idx' in match.groupdict() else print(False) # False
Jan Christoph Terasa
  • 5,781
  • 24
  • 34