Python正则表达式:检测单词是否存在

3

我希望能够使用Python正则表达式检测一个句子中是否包含某个单词,并且希望能够对其取反。

import re
re.match(r'(?=.*\bfoo\b)', 'bar red foo here')

这段代码可以工作,但我不明白为什么需要在其中放置.*。 而要对其取反,我不知道该怎么做。 我尝试过:

re.match(r'(?!=.*\bfoo\b)', 'bar red foo here')

但是它不能工作。 我最终的目标是将它们结合在一起,如下所示:
re.match(r'(?=.*\bfoo\b)(?!=.*\bbar\b)', 'bar red foo here')

5
为什么不使用类似于 'foo' in sentence and 'bar' not in sentence 的语法呢?(或者你想要实现的其他操作) - Two-Bit Alchemist
re.match(r'\bfoo\b', s) and not re.match(r'\bbar\b', s) - L3viathan
@L3viathan,我看不出你为什么会更喜欢使用这个而不是s中的'substring',你能解释一下吗? - Two-Bit Alchemist
2
@Two-BitAlchemist:如果你仔细看正则表达式,你会注意到\b——一个单词边界。尝试使用innot in来模拟它。 - Wiktor Stribiżew
@WiktorStribiżew 是的,我熟悉这个。而且“单词边界”是由一个单词定义的,该单词是Unicode字母数字或下划线字符序列,这可能有点令人惊讶。 - Two-Bit Alchemist
显示剩余3条评论
3个回答

4
要检测一个字符串中是否存在某个单词,你需要使用“正向预查”。
(?=.*\bfoo\b)
< p > .* 是必要的,以便在字符串开始之外搜索更远(re.match 将搜索锚定在字符串开头)。

要检查字符串中是否没有单词,请使用 负向先行断言

(?!.*\bbar\b)
 ^^^

那么,将它们结合起来:
re.match(r'(?=.*\bfoo\b)(?!.*\bbar\b)', input)

这段代码会在一个字符串中查找整个单词foo,并且不包含整个单词bar


只是一个补充:如果字符串中包含换行符号,请使用re.Sre.DOTALL)标志编译正则表达式,这样.也可以匹配换行符。 - Wiktor Stribiżew
1
匹配包含“bar red foo”的行。 - user557597
Python的match()函数意味着BOS(而不是EOS)。这让Python成为了我列出的受限引擎之一。 - user557597
不需要提及Python名称。Python 3有一个re.fullmatch方法 - Wiktor Stribiżew
我确定这个正则表达式 (?=.*\bfoo\b)(?!.*\bbar\b) 在字符串 bar red foo 中匹配了某处。我在 Perl 中运行它,但在 Python 中未匹配成功,可能是由于引擎有缺陷。 - user557597

2
更新
刚发现Python的re.match()有一个隐含的^锚点。
换句话说,它只会在字符串开头匹配,
与Java不同的是,它不需要在整个字符串中都匹配。

但要注意,如果将正向和负向前瞻组合起来,
就像Stribnez的答案一样,如果没有锚定到
某些东西,就可能导致意外的结果。无论是文字还是BOS锚点^

对于一般用途,请不要依赖于,或者说在某些语言中
match()函数意味着BOS锚点^(可能还有EOS$)。
始终在其中放置一个(或两个)。这样可以使用
search()中也是如此。并且可移植到其他语言。

为了看到正向和负向的串联前瞻如何引起问题,
请考虑这个棘手的独立表达式(?=.*\bfoo\b)(?!.*\bbar\b)

可以像这样检查它:

由于它是串联的,两个断言必须在同一位置匹配
字符串。

在给定相同的字符串位置时,负断言
可以在发现下游不匹配其内容的位置时得到满足。

假设不存在任何锚定,这将留下一个开口处上游
(在示例中,在搜索位置和bar文字之间)
存在不需要的内容,仍然满足正/负断言对。

例如:
(?=.*\bfoo\b)(?!.*\bbar\b)
匹配
bar red foo

**  Grp 0 -  ( pos 1 , len 0 )  EMPTY 

b<here>ar red foo

这表明在位置1,两个断言都得到了满足。
结论: 1.始终使用锚点,即使它们是暗示的。 2.避免使用任何语言的match()函数,改用search()。
无论您使用正向先行断言还是负向先行断言,如果不使用正确的语法,它都无法正常工作。
看看这个(?!=.*\bfoo\b)。这意味着下一个字符不能是等号=,后面跟着一堆字符直到下一个foo。这是不允许的。因此,它将不匹配= ab foo,但它将匹配'=(here) ab foo'。
下一个问题是,如果您没有给断言提供任何锚定内容,它会使用一个bump-along来将位置移动到一个在其中字符之间满足它的位置。所需的负向先行断言更正如下:^(?!.*\bfoo\b)。
供参考:
(?=..)  Positive lookahead
(?<=..) Positive lookbehind
(?!..)  Negative lookahead
(?<!..) Negative lookbehind   

此外,它们可以混合和嵌套在任何地方。


1
你需要使用.*,因为re.match()尝试将模式与字符串开头匹配。如果你想搜索整个字符串,请使用re.search()
就像你可以使用if re.search(...):一样,你也可以使用if not re.search(...):

搜索仍然返回匹配项:re.search(r'(?=\bfoo\b)(?!\bred\b)', 'bar red foo here'),所以我猜我不能使用它。 - max

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接