re.findall 行为怪异
Posted
技术标签:
【中文标题】re.findall 行为怪异【英文标题】:re.findall behaves weird 【发布时间】:2022-01-04 06:39:21 【问题描述】:源字符串是:
# Python 3.4.3
s = r'abc123d, hello 3.1415926, this is my book'
这是我的模式:
pattern = r'-?[0-9]+(\\.[0-9]*)?|-?\\.[0-9]+'
但是,re.search
可以给我正确的结果:
m = re.search(pattern, s)
print(m) # output: <_sre.SRE_Match object; span=(3, 6), match='123'>
re.findall
只是转储一个空列表:
L = re.findall(pattern, s)
print(L) # output: ['', '', '']
为什么re.findall
不能给我预期的名单:
['123', '3.1415926']
【问题讨论】:
将捕获组转为非捕获组。 @AvinashRaj,嗯..,如果我删除那个捕获组,即使 re.search 给我一个 None 结果 @stribizhev,不是,'3.1415926' 应该是结果中的浮点数 @O'Skywalker 尝试使用 puttern 像 -?\d?\.?\d+ 一些在线网站可以帮助调试,例如texttoolkit.com/re.findall 【参考方案1】:这里有两点需要注意:
re.findall
如果正则表达式模式包含捕获组,则返回捕获的文本
模式中的 r'\\.'
部分匹配两个连续的字符,\
和除换行符以外的任何字符。
见findall
reference:
如果模式中存在一个或多个组,则返回组列表;如果模式有多个组,这将是一个元组列表。结果中包含空匹配项,除非它们触及另一个匹配项的开头。
注意要让re.findall
返回匹配值,你通常可以
(a(b)c)
-> abc
)
将所有捕获组转换为non-capturing(即,将(
替换为(?:
)除非存在引用模式中组值的反向引用(然后见下文)
改用re.finditer
([x.group() for x in re.finditer(pattern, s)]
)
在您的情况下,findall
返回所有捕获的空文本,因为您在 r''
字符串文字中有 \\
试图匹配文字 \
。
要匹配数字,您需要使用
-?\d*\.?\d+
正则表达式匹配:
-?
- 可选减号
\d*
- 可选数字
\.?
- 可选的小数分隔符
\d+
- 1 个或多个数字。
见demo
这里是IDEONE demo:
import re
s = r'abc123d, hello 3.1415926, this is my book'
pattern = r'-?\d*\.?\d+'
L = re.findall(pattern, s)
print(L)
【讨论】:
【参考方案2】:s = r'abc123d, hello 3.1415926, this is my book'
print re.findall(r'-?[0-9]+(?:\.[0-9]*)?|-?\.[0-9]+',s)
您在使用原始模式时不需要转义两次。
输出:['123', '3.1415926']
返回类型也是字符串的列表。如果您希望返回类型为 integers 和 floats,请使用 map
import re,ast
s = r'abc123d, hello 3.1415926, this is my book'
print map(ast.literal_eval,re.findall(r'-?[0-9]+(?:\.[0-9]*)?|-?\.[0-9]+',s))
输出:[123, 3.1415926]
【讨论】:
虽然这个正则表达式的效率不如我的,但我承认ast
的技巧很酷(尽管在 OP 中不需要)。
@stribizhev 我读了他的一个 cmets....@stribizhev, it's not, '3.1415926' should be a float number in the result
所以我把它包括在我的回答中:)
你们两个都是天才,我很难选择接受哪一个。 :)
您也可以像这样使用第一个字符区分来减少步骤:(?=[-\d.])-?(?:\d+(?:\.\d*)?|\.\d+)
【参考方案3】:
只是为了解释为什么您认为 search
返回了您想要的结果而 findall
没有返回?
搜索返回一个SRE_Match
对象,其中包含一些信息,例如:
string
: 属性包含传递给搜索函数的字符串。
re
: REGEX
用于搜索功能的对象。
groups()
:REGEX
内的捕获组捕获的字符串列表。
group(index)
:使用index > 0
按组检索捕获的字符串。
group(0)
:返回与REGEX
匹配的字符串。
search
停止当它找到第一个机器构建SRE_Match
对象并返回它,检查此代码:
import re
s = r'abc123d'
pattern = r'-?[0-9]+(\.[0-9]*)?|-?\.[0-9]+'
m = re.search(pattern, s)
print(m.string) # 'abc123d'
print(m.group(0)) # REGEX matched 123
print(m.groups()) # there is only one group in REGEX (\.[0-9]*) will empy string tgis why it return (None,)
s = ', hello 3.1415926, this is my book'
m2 = re.search(pattern, s) # ', hello 3.1415926, this is my book'
print(m2.string) # abc123d
print(m2.group(0)) # REGEX matched 3.1415926
print(m2.groups()) # the captured group has captured this part '.1415926'
findall
的行为不同,因为它不仅会在找到第一个马赫时停止,它会一直提取直到文本结尾,但如果 REGEX
包含至少一个捕获组 findall
不要返回匹配的字符串,但捕获组捕获的字符串:
import re
s = r'abc123d , hello 3.1415926, this is my book'
pattern = r'-?[0-9]+(\.[0-9]*)?|-?\.[0-9]+'
m = re.findall(pattern, s)
print(m) # ['', '.1415926']
第一个element
是在第一个马赫被发现时返回,女巫是'123'
捕获组仅捕获''
,但第二个element
在第二个匹配中捕获'3.1415926'
捕获组匹配此部分'.1415926'
。
如果您想让findall
返回匹配的字符串,您应该将REGEX
中的所有捕获组()
设为非捕获组(?:)
:
import re
s = r'abc123d , hello 3.1415926, this is my book'
pattern = r'-?[0-9]+(?:\.[0-9]*)?|-?\.[0-9]+'
m = re.findall(pattern, s)
print(m) # ['123', '3.1415926']
【讨论】:
以上是关于re.findall 行为怪异的主要内容,如果未能解决你的问题,请参考以下文章