【问题标题】:Extract named group regex pattern from a compiled regex in Python从 Python 中编译的正则表达式中提取命名组正则表达式模式
【发布时间】:2018-07-29 05:27:09
【问题描述】:

我在 Python 中有一个包含多个命名组的正则表达式。但是,如果先前的组已匹配,则可能会错过匹配一组的模式,因为似乎不允许重叠。举个例子:

import re
myText = 'sgasgAAAaoasgosaegnsBBBausgisego'
myRegex = re.compile('(?P<short>(?:AAA))|(?P<long>(?:AAA.*BBB))')

x = re.findall(myRegex,myText)
print(x)

产生输出:

[('AAA', '')]

“长”组未找到匹配项,因为“AAA”在为前面的“短”组寻找匹配项时已用尽。

我试图找到一种允许重叠的方法,但失败了。作为替代方案,我一直在寻找一种方法来分别运行每个命名组。类似于以下内容:

for g in myRegex.groupindex.keys():
    match = re.findall(***regex_for_named_group_g***,myText)

是否可以为每个命名组提取正则表达式?

最终,我想生成一个字典输出(或类似的),例如:

{'short':'AAA',
 'long':'AAAaoasgosaegnsBBB'}

我们将不胜感激地接受任何和所有建议。

【问题讨论】:

  • 没有正则表达式引擎允许在同一位置测试两个有效匹配项。不过,您可以在模式中使用重叠组,例如 in this demo
  • 感谢您提供信息和链接 - 该解决方案非常有趣。但是,我将使用的正则表达式是由一个简单的算法生成的,并且每个命名组的结构都由一个“|”分隔。 (OR) 字符。因此,在这种情况下嵌套正则表达式是不可行的。但仍然是一个非常有用的提示。
  • 好的,所以你必须单独运行正则表达式,或者如果可能的话完全放弃正则表达式。
  • @Wiktor Stribiżew 是的,我认为你是对的。我添加了一些技巧来尝试自动化单独运行正则表达式并将结果整理到字典中的过程。

标签: python regex python-3.x


【解决方案1】:

似乎没有一个明显的答案,所以这里有一个 hack。它需要一些技巧,但基本上它将原始正则表达式拆分为其组成部分,并在原始文本上分别运行每个组正则表达式。

import re

myTextStr = 'sgasgAAAaoasgosaegnsBBBausgisego'
myRegexStr = '(?P<short>(?:AAA))|(?P<long>(?:AAA.*BBB))'
myRegex = re.compile(myRegexStr)   # This is actually no longer needed

print("Full regex with multiple groups")
print(myRegexStr)

# Use a regex to split the original regex into separate regexes
# based on group names
mySplitGroupsRegexStr = '\(\?P<(\w+)>(\([\w\W]+?\))\)(?:\||\Z)'
mySplitGroupsRegex = re.compile(mySplitGroupsRegexStr)
mySepRegexesList = re.findall(mySplitGroupsRegex,myRegexStr)

print("\nList of separate regexes")
print(mySepRegexesList)

# Convert separate regexes to a dict with group name as key
# and regex as value
mySepRegexDict = {reg[0]:reg[1] for reg in mySepRegexesList}
print("\nDictionary of separate regexes with group names as keys")
print(mySepRegexDict)

# Step through each key and run the group regex on the original text.
# Results are stored in a dictionary with group name as key and
# extracted text as value.
myGroupRegexOutput = {}
for g,r in mySepRegexDict.items():
    m = re.findall(re.compile(r),myTextStr)
    myGroupRegexOutput[g] = m[0]

print("\nOutput of overlapping named group regexes")
print(myGroupRegexOutput)

结果输出是:

Full regex with multiple groups
(?P<short>(?:AAA))|(?P<long>(?:AAA.*BBB))

List of separate regexes
[('short', '(?:AAA)'), ('long', '(?:AAA.*BBB)')]

Dictionary of separate regexes with group names as keys
{'short': '(?:AAA)', 'long': '(?:AAA.*BBB)'}

Output of overlapping named group regexes
{'short': 'AAA', 'long': 'AAAaoasgosaegnsBBB'}

这可能对某个地方的某人有用。

【讨论】:

    【解决方案2】:

    似乎没有更好的方法来做到这一点,但这是另一种方法,类似于this other answer,但更简单一些。它将起作用,前提是 a)您的模式将始终形成为一系列由管道分隔的命名组,并且 b)命名组模式本身不包含命名组。

    如果您对每个模式的所有匹配项感兴趣,以下是我的方法。 re.split 的参数查找后跟(?=&lt; 的文字管道,即命名组的开头。它编译每个子模式并使用groupindex 属性来提取名称。

    def nameToMatches(pattern, string):
        result = dict()
        for subpattern in re.split('\|(?=\(\?P<)', pattern):
            rx = re.compile(subpattern)
            name = list(rx.groupindex)[0]
            result[name] = rx.findall(string)
        return result
    

    使用给定的文本和模式,返回{'long': ['AAAaoasgosaegnsBBB'], 'short': ['AAA']}。完全不匹配的模式将有一个空列表来表示它们的值。

    如果你只希望每个模式匹配一​​个,你可以让它更简单一点:

    def nameToMatch(pattern, string):
        result = dict()
        for subpattern in re.split('\|(?=\(\?P<)', pattern):
            match = re.search(subpattern, string)
            if match:
                result.update(match.groupdict())
        return result
    

    这会为您提供{'long': 'AAAaoasgosaegnsBBB', 'short': 'AAA'}。如果其中一个命名组根本不匹配,它将不在字典中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-21
      • 1970-01-01
      • 2013-01-23
      • 1970-01-01
      • 1970-01-01
      • 2011-04-21
      相关资源
      最近更新 更多