re.findall 行为怪异

Posted

技术标签:

【中文标题】re.findall 行为怪异【英文标题】:re.findall behaves weird 【发布时间】:2015-08-10 08:33:39 【问题描述】:

源字符串是:

# 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']

返回类型也是字符串的列表。如果您希望返回类型为 integersfloats,请使用 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 &gt; 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 行为怪异的主要内容,如果未能解决你的问题,请参考以下文章

re.findall 行为怪异

re.findall 行为怪异

re.findall 不返回完整匹配?

re.match re.search re.findall区别

在 python re.findall 中使用多个标志

Python中的re.findall('(\d)\\1+','33acad122')怎么理解,求详