regex 回复:findall没有返回完整的比赛?

ttygqcqt  于 12个月前  发布在  其他
关注(0)|答案(7)|浏览(132)

我有一个文件,其中包括一堆字符串,如“大小=XXX;“.我第一次尝试Python的re模块,对以下行为有点困惑:如果在正则表达式中使用管道表示“或”,则只能看到返回匹配位。例如:

>>> myfile = open('testfile.txt', 'r').read()
>>> re.findall('size=50;', myfile)
['size=50;', 'size=50;', 'size=50;', 'size=50;']

>>> re.findall('size=51;', myfile)
['size=51;', 'size=51;', 'size=51;']

>>> re.findall('size=(50|51);', myfile)
['51', '51', '51', '50', '50', '50', '50']

>>> re.findall(r'size=(50|51);', myfile)
['51', '51', '51', '50', '50', '50', '50']

匹配的“size=”部分已经消失了(但它肯定会在搜索中使用,否则会有更多的结果)。我做错了什么?

xmq68pz9

xmq68pz91#

您遇到的问题是,如果re.findall试图匹配的正则表达式捕获了组(即,正则表达式中括在括号中的部分),则返回的是组,而不是匹配的字符串。
解决此问题的一种方法是使用非捕获组(前缀为?:)。

>>> import re
>>> s = 'size=50;size=51;'
>>> re.findall('size=(?:50|51);', s)
['size=50;', 'size=51;']

如果re.findall尝试匹配的正则表达式没有捕获任何内容,则返回整个匹配字符串。
尽管在这种特殊情况下使用character classes可能是最简单的选择,但非捕获组提供了更通用的解决方案。

vaqhlq81

vaqhlq812#

当正则表达式包含括号时,它们会将其内容 * 捕获 * 到组中,将findall()的行为更改为仅返回这些组。以下是文档中的相关部分:
(...)
匹配括号内的任何正则表达式,并指示组的开始和结束;在执行匹配之后,可以检索组的内容,并且稍后可以在字符串中与\number特殊序列进行匹配,如下所述。要匹配文字'('')',请使用\(\),或将它们包含在字符类中:[(] [)]
为了避免这种行为,你可以使用一个 * 非捕获 * 组:

>>> re.findall(r'size=(?:50|51);',myfile)
['size=51;', 'size=51;', 'size=51;', 'size=50;', 'size=50;', 'size=50;', 'size=50;']

再一次,从docs:
(?:...)
一个非捕获版本的普通括号。匹配括号内的任何正则表达式,但在执行匹配或稍后在模式中引用后,无法检索该组匹配的子字符串。

kokeuurv

kokeuurv3#

在某些情况下,非捕获组是不合适的,例如使用正则表达式检测重复的单词(来自python文档的示例)

r'(\b\w+)\s+\1'

在这种情况下,要获得整个比赛,可以使用

[groups[0] for groups in re.findall(r'((\b\w+)\s+\2)', text)]

请注意,\1已更改为\2

r6vfmomb

r6vfmomb4#

正如其他人提到的,re.findall的“问题”是它根据捕获组的使用返回字符串/字符串元组的列表。如果您不想更改正在使用的捕获组(不使用字符组[]或非捕获组(?:)),则可以使用finditer而不是findall。这给出了 * Match对象 * 的 * 迭代器 *,而不仅仅是字符串。所以现在你可以获取完整的匹配,即使使用捕获组:

import re

s = 'size=50;size=51;'
for m in re.finditer('size=(50|51);', s):
    print(m.group())

将给予:

size=50;
size=51;

如果你需要一个列表,类似于findall,你可以使用一个list-comprehension:

>>> [m.group() for m in re.finditer('size=(50|51);', s)]
['size=50;', 'size=51;']
mfuanj7w

mfuanj7w5#

'size=(50|51);'表示您正在查找size=50size=51,但仅匹配5051部分(请注意括号),因此它不会返回sign=
如果你想返回sign=,你可以这样做:

re.findall('(size=50|size=51);',myfile)
xiozqbni

xiozqbni6#

我认为你想要的是使用[]而不是()[]表示一组字符,而()表示组匹配。试试这样的方法:

re.findall('size=5[01];', myfile)
bxfogqkk

bxfogqkk7#

您可以使用re.finditer,它也以Match对象iterrator的形式返回所有匹配的值。方法.group()返回整个字符串。因此,您的代码可能看起来像下一种方式,

myfile = open('testfile.txt', 'r').read()
matches = list(re.finditer('size=(50|51);', myfile))
matches[0].group() # size=50

相关问题