获取Python匹配对象中的所有未命名组

时间:2015-05-17 23:10:45

标签: python regex

我有许多使用命名组和未命名组的相关正则表达式。我想将未命名的组作为位置参数插入到使用命名组选择的函数中。

例如,如果模式([abc]+)([123]+)(?P<end>[%#])与字符串"aaba2321%"匹配,我希望获得包含["aaba", "2321"]的列表,但 {{1} }

我尝试了以下内容:

"%"

假设它不会捕获命名组,因为有一个单独的方法match_obj.groups() ,仅用于获取命名组。不幸的是,groupdict包含了命名组。

然后,我决定为它编写自己的生成器:

groups

不幸的是,命名组也可以作为编号组访问。我如何单独获得编号组?

2 个答案:

答案 0 :(得分:1)

有一种可怕的方式来做你要求的事情。它涉及通过跨度(开始和结束索引)索引所有匹配并删除groupdictgroups中出现的匹配:

named = dict()
unnamed = dict()
all = mo.groups()

# Index every named group by its span
for k,v in mo.groupdict().items():
    named[mo.span(k)] = v

# Index every other group by its span, skipping groups with same 
# span as a named group
for i,v in enumerate(all):
    sp = mo.span(i + 1)
    if sp not in named:
        unnamed[sp] = v

print(named)   # {(8, 9): '%'}
print(unnamed) # {(4, 8): '2321', (0, 4): 'aaba'}

跨度索引的原因是必要的,因为未命名和命名的组可以具有相同的值。组的唯一唯一标识符是它的开始和结束位置,因此即使您具有相同值的组,此代码也能正常工作。这是一个演示:http://ideone.com/9O7Hpb

另一种方法是编写一个函数,按照问题中显示的形式转换正则表达式,其中所有以前未命名的正则表达式都用一些前缀和数字命名。您可以匹配此正则表达式并从groupdict

中选择名称以前缀开头的组

答案 1 :(得分:0)

这是一个干净的版本,使用re.regex.groupindex

  

(?P<id>)定义的任何符号组名映射到组号的字典。


TL; DR:简短复制和粘贴功能:

def grouplist(match):
    named = match.groupdict()
    ignored_groups = set()
    for name, index in match.re.groupindex.items():
        if name in named:  # check twice, if it is really the named attribute.
            ignored_groups.add(index)
    return [group for i, group in enumerate(match.groups()) if i+1 not in ignored_groups]


m = re.match('([abc]+)([123]+)(?P<end>[%#])', "aaba2321%")

unnamed = grouplist(m)
print(unnamed)

完整示例

使用groupindex,我们可以获得命名匹配项的索引,并在构建最终的组列表时将其排除在外,在下面的代码中称为unnamed

import re

# ===================================================================================
# This are the current matching groups:
# ===================================================================================
regex = re.compile("(((?P<first_name>\w+)) (?P<middle_name>\w+)) (?P<last_name>\w+)") 
#                   |-------------------- #1 ------------------|
#                    |------- #2 -------|
#                     |------ #3 ------|
#                                          |------- #4 -------|
#                                                                |------ #5 ------|
# ===================================================================================
# But we want to have the following groups instead (regex line is identical):
# ===================================================================================
regex = re.compile("(((?P<first_name>\w+)) (?P<middle_name>\w+)) (?P<last_name>\w+)")
#                   |---------------- #1 (#1) -----------------|
#                    |- first_name (#2) -|
#                     |---- #2 (#3) ----|
#                                          |- middle_name (#4)-|
#                                                                | last_name (#5) |

m = regex.match("Pinkamena Diane Pie")

为方便起见,这是我们要使用的值:

assert list(m.groups()) == [
    'Pinkamena Diane',  # group #1
    'Pinkamena',        # group #2 (first_name)
    'Pinkamena',        # group #3
    'Diane',            # group #4 (middle_name)
    'Pie',              # group #5 (last_name)
]

assert dict(m.groupdict()) == {
    'first_name':  'Pinkamena',  # group #2
    'middle_name': 'Diane',      # group #4
    'last_name':   'Pie',        # group #5
}

assert dict(m.re.groupindex) == {
    'first_name':  2,  # Pinkamena
    'middle_name': 4,  # Diane
    'last_name':   5,  # Pie
}

因此,我们现在可以将这些命名组的索引存储在ignored_groups集中,以在用unnamed填充m.groups()时省略这些组:

named = m.groupdict()
ignored_groups = set()
for name, index in m.re.groupindex.items():
    if name in named:  # check twice, if it is really the named attribute.
        ignored_groups.add(index)
    # end if
unnamed = [group for i, group in enumerate(m.groups()) if i+1 not in ignored_groups]
# end for

print(unnamed)
print(named)

所以最终我们得到:

# unnamed = grouplist(m)
assert unnamed == [
    'Pinkamena Diane',  # group #1 (#1)
    'Pinkamena',        # group #2 (#3)
]

# named = m.groupdict()
assert named == {
    'first_name':  'Pinkamena',  # group #2
    'middle_name': 'Diane',      # group #4
    'last_name':   'Pie',        # group #5
}

亲自尝试示例:https://ideone.com/pDMjpP