有没有办法在正则表达式中以任意顺序匹配一组分组?
我看了一下相关的问题,发现有不少,但我觉得没有一个能回答我的问题。我对正则表达式(Regex)还很陌生,但我想学得更好,所以请耐心点。我想在一个字符串中匹配几个组,但顺序可以不固定。这种情况我应该用正则表达式吗?如果可以的话,应该怎么做?如果有关系的话,我打算在IronPython中使用这些。
补充说明:有人让我更具体一点,所以这里是详细信息:
我想用 re.match
和一个像这样的正则表达式:
\[image\s*(?(@alt:(?<alt>.*?);).*(@title:(?<title>.*?);))*.*\](?<arg>.*?)\[\/image\]
但是它只会在这些命名组按正确顺序并且用空格分开的情况下匹配。我希望能在任何顺序下匹配这些命名组,只要它们出现在正则表达式中现在的位置。
一个典型的字符串可能是这样的:
[image @alt:alien; @title:reddit alien;]http://www.reddit.com/alien.png[/image]
但我应该也能匹配:
[image @title:reddit alien; @alt:alien;]http://www.reddit.com/alien.png[/image]
所以这些“属性”(在第一个“标签”中,@和;之间的内容)应该能以任何顺序匹配,只要它们都出现。
2 个回答
你标题中的问题答案是“不”——要匹配N组“无论顺序如何”,正则表达式中需要有一个“或”符号(|
),这意味着你需要考虑所有可能的N组排列组合,总共有
解决方案可能有很多,具体取决于你希望用什么来分隔这些组。假设,比如说,你不在乎分隔符是什么(如果你在乎,请在问题中提供更详细的说明)。
在这种情况下,如果重叠匹配不可能或者你可以接受重叠,可以为每个组创建N个独立的正则表达式——假设这N个编译好的正则表达式对象放在一个名为grps
的列表中,那么
mos = [g.search(thestring) for g in grps]
就是这些组的匹配对象列表(对于没有匹配的组,值为None
)。通过mos
列表,你可以进行各种检查和进一步的操作,比如all(mos)
只有在所有组都匹配时才会返回True
,在这种情况下,[m.group() for m in mos]
就是匹配到的子字符串列表,等等。
如果你需要不重叠的匹配,那就稍微复杂一些——你可能需要提取每个组所有可能匹配的边界,然后看看是否能从这
所以,请先修改你的问题,提供更准确的说明,然后我们或许可以更清楚地为你提供所需的代码和/或算法。
编辑:我看到提问者现在至少澄清了提供示例的范围——不过,令人困惑的是,他提供了一个正则表达式模式示例和一个应该不匹配的字符串示例,无论顺序如何(正则表达式指定了一个子字符串@title
,而示例字符串中没有这个子字符串——真让人费解!)。
无论如何,如果示例中的组数(两个看起来可以互换,一个似乎必须出现在特定位置)代表了提问者实际的问题,那么感兴趣的排列总数只有两个,因此用一个竖线|
将这“两个”排列连接起来当然是可行的。不过,提问者的实际问题真的是这样吗……?
编辑:如果感兴趣的排列数量很小,这里有一个避免在模式中重复组名问题的方法示例(语法要求Python 2.7或更高版本,但这只是为了最后的“字典推导”,在许多早期版本的Python中也有相同的功能,只是语法不那么优雅dict(('a', ...
;-)...:
>>> r = re.compile(r'(?P<a1>a.*?a).*?(?P<b1>b.*?b)|(?P<b2>b.*?b).*?(?P<a2>a.*?a)')
>>> m = r.search('zzzakkkavvvbxxxbnnn')
>>> g = m.groupdict()
>>> d = {'a':(g.get('a1') or g.get('a2')), 'b':(g.get('b1') or g.get('b2'))}
>>> d
{'a': 'akkka', 'b': 'bxxxb'}
这跟用正则表达式解析HTML时遇到的一个大问题很像——属性的顺序并不固定,而且很多标签有些意想不到的属性(比如 <br clear="all">
)。所以你在处理的标记语法也差不多。
Pyparsing 以一种间接的方式解决了这个问题——它不是试图解析所有可能的排列组合,而是解析一种通用的 "@attrname:属性值;" 语法,并在一个属性映射的数据结构中跟踪属性的键和值。这个映射让你可以轻松获取“title”属性,无论它是在图片标签的最前面还是最后面。这个功能已经内置在pyparsing的API方法中,比如makeHTMLTags和makeXMLTags。
当然,这种标记并不是XML,但类似的方法可以得到一些相对容易处理的结果:
text = """[image @alt:alien; @title:reddit alien;]http://www.reddit.com/alien1.png[/image]
But I should have no problem matching:
[image @title:reddit alien; @alt:alien;]http://www.reddit.com/alien2.png[/image]
"""
from pyparsing import Suppress, Group, Word, alphas, SkipTo, Dict, ZeroOrMore
LBRACK,RBRACK,COLON,SEMI,AT = map(Suppress,"[]:;@")
tagAttribute = Group(AT + Word(alphas) + COLON + SkipTo(SEMI) + SEMI)
imageTag = LBRACK + "image" + Dict(ZeroOrMore(tagAttribute)) + RBRACK
imageLink = imageTag + SkipTo("[/image]")("text")
for taginfo in imageLink.searchString(text):
print taginfo.alt
print taginfo.title
print taginfo.text
print
输出结果:
alien
reddit alien
http://www.reddit.com/alien1.png
alien
reddit alien
http://www.reddit.com/alien2.png