regex 在python中使用正则表达式提取单词

ubbxdtey  于 2023-03-04  发布在  Python
关注(0)|答案(4)|浏览(102)

我是Python的新手,想知道如何使用正则表达式。
假设我有一个

alice(ben)charlie(dent)elise(fiona)

grace

对于第一种情况,我想得到<alice,charlie,elise>
对于第二种情况,我希望得到grace
我试了下面的,但是只得到elise(fiona), elise

import re

foo = 'alice(ben)charlie(dent)elise(fiona)'
pattern = re.compile(r'((\w+)\(\w+\))+')

match = re.findall(pattern, foo)
print(match)
zf9nrax1

zf9nrax11#

一个常见的初学者错误是期望一个重复的捕获括号返回所有捕获的与re.findall的匹配。正如您所发现的,它在找到所有匹配项后返回最后一个组。
更详细地说,你的正则表达式说“找到尽可能多的括号中的表达式”,findall在一次迭代中找到了所有的表达式,这时,最后一个匹配是括号中的内容,这就是捕获组返回的结果。
只需删除最后一个+就可以改变行为,这样findall将返回每个匹配项,但不会向前跳过,从而允许下一次迭代找到下一个匹配项。

>>> re.findall(r'((\w+)\(\w+\))', 'alice(ben)charlie(dent)elise(fiona)')
[('alice(ben)', 'alice'), ('charlie(dent)', 'charlie'), ('elise(fiona)', 'elise')]

现在,您将告诉findall一次查找并返回一个匹配项;因为你找到了所有的匹配项,所以它将从找到前一个匹配项的地方重新开始搜索,并返回每次迭代中匹配的任何项。
根据您的特殊情况,您可以使文字括号可选,或者指定尾随上下文应该是什么。

>>> re.findall(r'(\w+)(?:\(\w+\))?', 'grace')
['grace']
>>> re.findall(r'(\w+)(?:\(\w+\))?', 'alice(ben)charlie(dent)elise(fiona)')
['alice', 'charlie', 'elise']

由于您实际上并不需要捕获整个表达式,所以我也删除了最外面的捕获括号。

epfja78i

epfja78i2#

假设您希望捕获前面没有左括号或后面没有右括号的单词,可以使用(?<!\()\b\w+\b(?!\))

re.findall(r'(?<!\()\b\w+\b(?!\))', 'alice(ben)charlie(dent)elise(fiona)')
# ['alice', 'charlie', 'elise']

re.findall(r'(?<!\()\b\w+\b(?!\))', 'grace')
# ['grace']

re.findall(r'(?<!\()\b\w+\b(?!\))', '(ben)charlie(dent)elise(fiona)')
# ['charlie', 'elise']

regex demo

(?<!\()\b  # match a word boundary not preceded by "("
\w+        # match word character(s)
\b(?!\))   # match a word boundary not followed by ")"
kuarbcqp

kuarbcqp3#

我希望我理解你的问题是正确的,我做了一个正则表达式来解决这个问题:).模式应该是(?:([a-zA-Z]+)(?:\(.+?\))?),捕获组id应该是1,对于括号外的名称.如果你想玩这个正则表达式,更好地理解它,使用这个链接:
https://regexr.com/79fct

w1jd8yoj

w1jd8yoj4#

模式r'((\w+)\(\w+\))+'的最后一个+使模式匹配整个字符串一次:

>>> re.search(pattern, foo)
<re.Match object; span=(0, 35), match='alice(ben)charlie(dent)elise(fiona)'>

因此,re.findall结果有一个包含单个结果('elise(fiona)', 'elise')的列表,它是该匹配的所有.groups

>>> re.search(pattern, foo).groups()
('elise(fiona)', 'elise')

如果我们从模式中删除+,它只匹配一个普通/括号对:

>>> pattern = re.compile(r'((\w+)\(\w+\))')
>>> re.search(pattern, foo)
<re.Match object; span=(0, 10), match='alice(ben)'>

findall将给予多个结果:

>>> re.findall(pattern, foo)
[('alice(ben)', 'alice'), ('charlie(dent)', 'charlie'), ('elise(fiona)', 'elise')]

要使findall的结果成为普通字符串,正则表达式最多需要有一个捕获组:

>>> test = 'xxxxxx'
>>> re.findall('xx', test)
['xx', 'xx', 'xx']
>>> re.findall('x(x)', test)
['x', 'x', 'x']
>>> re.findall('(x)(x)', test)
[('x', 'x'), ('x', 'x'), ('x', 'x')]

如果没有捕获组,它将显示整个匹配;如果为1,则显示该捕获组匹配的部分(对于每个匹配);对于多个捕获组,它显示所有捕获组匹配的元组。
要解决这个问题,不要使用额外的捕获组,我们可以使用非捕获组,但在当前的情况下,问题是一对额外的括号,这是完全没有必要的(至少现在+已经没有了):

>>> pattern = re.compile(r'(\w+)\(\w+\)')
>>> re.findall(pattern, foo)
['alice', 'charlie', 'elise']

但是,这对于第二个示例仍然不起作用:

>>> re.findall(pattern, 'grace')
[]

这是因为,如果字符串中没有带括号的名称,正则表达式就不匹配。当然,我们可以通过将模式的这一部分设置为可选来解决这个问题--这现在确实需要一个非捕获组:

>>> pattern = re.compile(r'(\w+)(?:\(\w+\))?')
>>> re.findall(pattern, foo)
['alice', 'charlie', 'elise']
>>> re.findall(pattern, 'grace')
['grace']

另一种方法是以完全不同的方式思考问题:将括号中的名称作为要拆分的数据的 * 分隔符 *。

>>> re.split('\(\w+\)', foo)
['alice', 'charlie', 'elise', '']
>>> re.split('\(\w+\)', 'grace')
['grace']

这在第一种情况下会在末尾提供一个额外的空字符串-它表示字符串中(fiona)匹配之后的所有文本。

相关问题