为什么(?:…)regex对re.match和re.findall的行为不同?



我正在研究一个CS50问题,你必须找到连续重复的字符串模式(DNA序列中的核苷酸)。我想我会使用re,因为它是Python3的分配,我已经用一点之前,并认为我可以弄清楚…但是不。

所以我搜索了如何才能完成这个任务,并找到了一个用户建议使用re.findall("(?:<pattern>)+", <string>)的主题,这正是我需要解决的问题。

所以我对?:表达感到好奇,并查阅了文档,但无法理解non-capturing group意味着什么,并且再次,当我发现使用由网址组成的字符串解释时,不得不四处寻找答案。

在这个特定的例子中,用户使用的是re.match:

"(?:https?|ftp)://(stackoverflow.com)"
output:
group1: (stackoverflow.com)
vs
"(https?|ftp)://(stackoverflow.com)"
output:
group1: (https)
group2: (stackoverflow.com)
在这一点上,我明白了什么是非捕获的意思…但是现在我不明白为什么?:re.matchre.findall的行为不同,其中匹配对象,如官方文档中所述,不捕获指示组,而从re.findall方法返回的列表似乎捕获匹配子字符串分组连续重复为一个。

我最好的猜测是,因为它是非捕获的方法继续寻找匹配的子字符串连续,一旦它结束它"关闭";组,这就是为什么重复的子字符串被分组为一个。但我仍然不知道为什么re.findall返回不应该被捕获的东西,如果有人能指出我在正确的方向,我真的很感激。

regex中的'捕获组'通常用括号表示。在像这样的正则表达式中:

<before>(<capture>)<after>

"捕获组"是(<capture>),这意味着这是我们实际想要的字符串的部分-正则表达式的其余部分是针对它的。我们需要它来捕获,例如,"数字后面跟着单词'美元'":

(d+)s*dollar

re.match()返回一个Match对象,它有几个"组",你可以用.group(n)方法访问。

  • Group 0总是包含整个匹配字符串,无论其中是否有任何'match groups'。在上面的例子中,与字符串"35 dollar item"匹配,它将是' "35美元"
  • n > 0包含正则表达式中的第n个捕获组。在上面的例子中,与字符串"35 dollar item"匹配,它将是"35"

re.findall()的行为根据regex中捕获组的数量而不同:

  • 如果没有捕获组,返回匹配整个正则表达式的所有字符串的列表(例如.group(0))
  • 如果只有一个捕获组,返回与捕获组匹配的所有字符串的列表(例如.group(1))
  • 如果有多个捕获组,返回(.group(1), .group(2), ..., .group(n))的元组列表。

无组织的想法是,有时我们想使用需要括号的表达式来消除歧义。最常见的是|。例如,如果我想要捕捉以"先生"称呼的某人的姓氏(而不是头衔);或者"Ms.",我可以这样写一个正则表达式:

(?:Mr|Ms).s*(w+)

(?:Mr|Ms)是一个非捕获组,因此它不会被视为它自己的组(例如.group(1))。同时,(w+)捕获组,所以将视为自己的集团。从这里,我们可以观察到re.match()re.findall()的行为,并看到它们与它们的文档一致:

>>> regex = r'(?:Mr|Ms).s*(w+)'
>>> mo = re.match(regex, 'Mr. Big and Ms. Small')
>>> mo.group(0)
'Mr. Big'
>>> mo.group(1)
'Big'
>>> re.findall(regex, 'Mr. Big and Ms. Small')
['Big', 'Small']

因为只有一个捕获组,re.findall()忽略组0。

对比以下行为,将MrMs注册为捕获组,从而产生不同的输出(因为现在有多个捕获组,所以re.findall()更改了其输出格式:

>>> regex2 = r'(Mr|Ms).s*(w+)'  # omitting the ?:
>>> re.findall(regex2, 'Mr. Big and Ms. Small')
[('Mr', 'Big'), ('Ms', 'Small')]

最新更新