我对正则表达式还是比较新的。我正在尝试找到与特定模式匹配的最短文本字符串,但如果最短模式是较大匹配的子字符串,则会遇到问题。例如:
import re
string = "A|B|A|B|C|D|E|F|G"
my_pattern = 'a.*?b.*?c'
my_regex = re.compile(my_pattern, re.DOTALL|re.IGNORECASE)
matches = my_regex.findall(string)
for match in matches:
print match
打印:
A|B|A|B|C
但我想要它返回:
A|B|C
有没有办法做到这一点,而不必遍历每个匹配,看它是否包含匹配的子字符串?
答案 0 :(得分:12)
与此处的大多数其他答案相反,此可以使用带有positive lookahead assertion的capturing group在单个正则表达式中完成:
>>> my_pattern = '(?=(a.*?b.*?c))'
>>> my_regex = re.compile(my_pattern, re.DOTALL|re.IGNORECASE)
>>> matches = my_regex.findall(string)
>>> print min(matches, key=len)
A|B|C
findall()
会返回所有可能的匹配项,因此您需要min()
才能获得最短的匹配项。
这是如何运作的:
答案 1 :(得分:1)
没有。 Perl返回最长的最左边的匹配,同时服从你的非贪婪量词。你不得不循环,我害怕。
编辑:是的,我意识到我上面说的是Perl,但我相信Python也是如此。
答案 2 :(得分:1)
另一种正则表达式解决方案;它只找到。* a。* b。* c:
的最后一次出现my_pattern = 'a(?!.*a.*b.*c).*b[^c]*c'
a(?!.*a.*?b.*?c)
确保首先“A”后没有'a.*?b.*?c'
A | A | B | C或A | B | A | B | C或A | B | C | A | B | C等字符串被删除
b[^c]*c
确保'B'后只有一个'C'
结果中的字符串如A | B | C | B | C或A | B | C | C被消除
所以你有最小的匹配'a.*?b.*?c'
答案 3 :(得分:0)
正则表达式引擎从字符串的开头开始搜索,直到找到匹配然后退出。因此,如果它在考虑较小的匹配之前找到匹配,则无法强制它在同一运行中考虑以后的匹配 - 您必须在子字符串上重新运行正则表达式。
设置全局标志并选择最短的匹配字符串将无济于事,因为从您的示例中可以看出 - 较短的匹配可能是另一个匹配的子字符串(或部分包含在其中)。我相信你必须从(1 +前一场比赛的索引)开始后续搜索并继续这样做。
答案 4 :(得分:0)
我不认为这个任务可以通过一个正则表达式完成。我没有证据证明是这种情况,但是有很多事情无法通过正则表达式完成,我预计这个问题就是其中之一。正则表达式局限性的一些很好的例子在this blog post中给出。
答案 5 :(得分:0)
这可能是sexegers的有用应用。正则表达式匹配偏向最长,最左边的选择。使用非贪婪量词(例如.*?
中的最长部分,并且反转输入和模式可以绕过最左边匹配的语义。
考虑以下根据需要输出A|B|C
的程序:
#! /usr/bin/env python
import re
string = "A|B|A|B|C|D|E|F|G"
my_pattern = 'c.*?b.*?a'
my_regex = re.compile(my_pattern, re.DOTALL|re.IGNORECASE)
matches = my_regex.findall(string[::-1])
for match in matches:
print match[::-1]
另一种方法是制定更严格的模式。假设您不希望重复已经看过的字符:
my_pattern = 'a[^a]*?b[^ab]*?c'
您的示例是通用的和做作的,但如果我们更好地了解您正在使用的输入,我们可以提供更好,更有帮助的建议。
答案 6 :(得分:0)
您可能能够以不包含较小匹配的方式编写正则表达式。
对于你的正则表达式:
a.*?b.*?c
我想你可以这样写:
a[^ab]*b[^c]*c
要弄清楚这一点很棘手,我没有看到任何更普遍或更明显正确的方法。 (编辑 - 早些时候我提出了一个负面的先行断言,但我没有办法让这种方法发挥作用。)
答案 7 :(得分:0)
一个Python循环来寻找最短的匹配,通过蛮力从左到右测试每个子字符串,选择最短的:
shortest = None
for i in range(len(string)):
m = my_regex.match(string[i:])
if m:
mstr = m.group()
if shortest is None or len(mstr) < len(shortest):
shortest = mstr
print shortest
另一个循环,这次让re.findall完成搜索所有可能匹配的艰苦工作,然后从右到左强力测试每个匹配,寻找更短的子字符串:
# find all matches using findall
matches = my_regex.findall(string)
# for each match, try to match right-hand substrings
shortest = None
for m in matches:
for i in range(-1,-len(m),-1):
mstr = m[i:]
if my_regex.match(mstr):
break
else:
mstr = m
if shortest is None or len(mstr) < len(shortest):
shortest = mstr
print shortest
答案 8 :(得分:0)
不,Python正则表达式引擎中没有。
我对自定义功能的看法:
import re, itertools
# directly from itertools recipes
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = itertools.tee(iterable)
for elem in b:
break
return itertools.izip(a, b)
def find_matches(rex, text):
"Find all matches, even overlapping ones"
matches= list(rex.finditer(text))
# first produce typical matches
for match in matches:
yield match.group(0)
# next, run it for any patterns included in matches
for match1, match2 in pairwise(matches):
subtext= text[match1.start()+1:match2.end()+1]
for result in find_matches(rex, subtext):
yield result
# also test the last match, if there was at least one
if matches:
subtext= text[matches[-1].start()+1:matches[-1].end()+1]
# perhaps the previous "matches[-1].end()+1" can be omitted
for result in find_matches(rex, subtext):
yield result
def shortest_match(rex, text):
"Find the shortest match"
return min(find_matches(rex, text), key=len)
if __name__ == "__main__":
pattern= re.compile('a.*?b.*?c', re.I)
searched_text= "A|B|A|B|C|D|E|F|G"
print (shortest_match(pattern, searched_text))