我想匹配字符串中最后一个简单模式,例如:
list = re.findall(r"\w+ AAAA \w+", "foo bar AAAA foo2 AAAA bar2")
print "last match: ", list[len(list)-1]
然而,如果字符串非常长,则会生成大量匹配项列表。有没有更直接的方法匹配第二个 "AAAA" 出现,或者我应该使用这个解决方法?
你可以使用表示行尾字符的$
:
>>> s = """foo bar AAAA
foo2 AAAA bar2"""
>>> re.findall(r"\w+ AAAA \w+$", s)
['foo2 AAAA bar2']
另外需要注意的是,list
是一个不好的变量名,因为它会遮盖内置类型。要访问列表的最后一个元素,您可以使用[-1]
索引:
>>> lst = [2, 3, 4]
>>> lst[-1]
4
re.findall('abc', 'def')
也会返回一个空列表 []
而不是 None
。 - Jason Goal你可以通过迭代所有匹配项并保留最后一个匹配项来避免构建列表:
def match_last(orig_string, re_prefix, re_suffix):
# first use positive-lookahead for the regex suffix
re_lookahead= re.compile(f"{re_prefix}(?={re_suffix})")
match= None
# then keep the last match
for match in re_lookahead.finditer(orig_string):
pass
if match:
# now we return the proper match
# first compile the proper regex…
re_complete= re.compile(re_prefix + re_suffix)
# …because the known start offset of the last match
# can be supplied to re_complete.match
return re_complete.match(orig_string, match.start())
return match
之后,match
存储最后一次匹配的结果或者None
。
只要提供了可能重叠的正则表达式部分作为re_suffix,例如\w+
,这对于所有pattern和searched string的组合都适用。
>>> match_last(
"foo bar AAAA foo2 AAAA bar2",
r"\w+ AAAA ", r"\w+")
<re.Match object; span=(13, 27), match='foo2 AAAA bar2'>
没有内置的re
库功能支持从右到左的字符串解析,输入字符串仅从左到右搜索模式。
然而,有一个PyPi regex模块支持此功能。它是regex.REVERSE
标志,或其内联变体(?r)
:
s="foo bar AAAA foo2 AAAA bar2"
print(regex.search(r"(?r)\w+ AAAA \w+$", s).group())
# => foo2 AAAA bar2
re
模块,可以通过^[\s\S]*
结构快速定位字符串结尾,并让回溯找到要捕获到单独组中的模式。然而,回溯可能会吞噬部分匹配(一旦所有后续模式匹配,它将停止产生更多文本),如果文本过大且没有匹配,则回溯可能变得灾难性。仅在输入字符串始终匹配或其长度较短且自定义模式不太依赖于回溯时才使用此技巧:print(re.search(r"(?:^[\s\S]*\W)?(\w+ AAAA \w+)$", s).group(1))
# => foo2 AAAA bar2
(?:^[\s\S]*\W)?
匹配一个可选序列,包括字符串开始、任意0个或多个字符和一个非单词字符 (\W
)。添加 \W
是为了使回溯返回到非单词字符,它必须是可选的,因为匹配可能从字符串的开头开始。请参见Python演示。import re
p = r"AAAA(?=\s\w+)" #revised per comment from @Jerry
p2 =r"\w+ AAAA \w+"
s = "foo bar AAAA foo2 AAAA bar2"
l = re.findall(p, s)
l2 = re.findall(p2, s)
print('l: {l}'.format(l=l))
#print(f'l: {l}') is nicer, but online interpreters sometimes don't support it.
# https://www.onlinegdb.com/online_python_interpreter
#I'm using Python 3.
print('l2: {l}'.format(l=l2))
for m in re.finditer(p, s):
print(m.span())
#A span of (n,m) would really represent characters n to m-1 with zero based index
#So.(8,12):
# => (8,11: 0 based index)
# => (9th to 12th characters conventional 1 based index)
print(re.findall(p, s)[-1])
输出:
l: ['AAAA', 'AAAA']
l2: ['bar AAAA foo2']
(8, 12)
(18, 22)
AAAA
(?=\w+\s)AAAA(?=\s\w+)
中要使用 (?=\w+\s)
?我觉得它实际上应该是一个正向回顾后发,即 (?<=\w+\s)AAAA(?=\s\w+)
。此外,考虑到 OP 提到的长字符串,使用 re.findall()
可能会使您的解决方案变慢。 - JerryAAAA(?=\s\w+)
可能匹配到某些内容,而 (?=\w+\s)AAAA(?=\s\w+)
则会避免匹配到这些内容? - Jerrysearch
和group
:>>> re.search('\w+ AAAA \w+$',"foo bar AAAA foo2 AAAA bar2").group(0)
'foo2 AAAA bar2'
它使用模式\w+ AAAA \w+$
,获取最后一个出现的与其相邻的单词和'AAAA'
一起,并且所有都使用\w+
(两次)和$
(一次)。
在模式匹配过程之后,您将需要使用_sre.SRE_Match.group
方法来获取_sre.SRE_Match
对象的所属值,并当然获取第零组(第一组),因为search
只保留一个匹配项(第零个)。
>>> timeit.timeit(lambda: re.findall(r"\w+ AAAA \w+$", s),number=1000000) # SilentGhost
5.783595023876842
>>> timeit.timeit('import re\nfor match in re.finditer(r"\w+ AAAA \w+", "foo bar AAAA foo2 AAAA bar2"):pass',number=1000000) # tzot
5.329235373691631
>>> timeit.timeit(lambda: re.search('\w+ AAAA \w+$',"foo bar AAAA foo2 AAAA bar2").group(0),number=1000000) # mine (U9-Forward)
5.441731174121287
>>>
我正在使用timeit
模块测试所有的时间,同时我将number=1000000
,这样会花费更长的时间。
mystr[::-1]
),然后查找反向模式的第一个出现位置。 - ChristopheD>>> timeit.timeit(stmt = 'regex.search(long_string)', setup = "import re; regex=re.compile('b'); long_string='a'*int(10e8)+'b'; reverse_string=long_string[::-1]", number=10)
8.432429309999861
- Him>>> timeit.timeit(stmt = 'regex.search(reverse_string)', setup = "import re; regex=re.compile('b'); long_string='a'*int(10e8)+'b'; reverse_string=long_string[::-1]", number=10)
3.3803000405896455e-05
- Him>>> timeit.timeit(stmt = 'regex.search(long_string)', setup = "import re; regex=re.compile('b$'); long_string='a'*int(10e8)+'b'; reverse_string=long_string[::-1]", number=10)
7.993536103000224
- Him