Python正则表达式:用于单词集合的交替匹配

5
我们知道\ba\b|\bthe\b将匹配单词"a"或"the"
我想建立一个正则表达式来匹配如下模式的字符串:

a/the/one reason/reasons for/of

这意味着我想匹配包含3个单词的字符串s

  • s的第一个单词应为"a"、"the"或"one"
  • s的第二个单词应为"reason"或"reasons"
  • s的第三个单词应为"for"或"of"

正则表达式\ba\b|\bthe\b|\bone\b \breason\b|reasons\b \bfor\b|\bof\b并没有帮助。

我该怎么做?顺便说一句,我使用的是Python。谢谢。

6个回答

5
你需要使用一个捕获组来拒绝混合OR运算符(|)。
(\ba\b|\bthe\b|\bone\b) (\breason\b|reasons\b) (\bfor\b|\bof\b)

更加优美的方式是在组周围放置单词边界。请注意,当您在正则表达式中使用空格时,无需使用单词边界。对于“reasons”和“reason”,您可以使用“?”使最后一个“s”变为可选项。如果您不想将单词配对作为单独的组进行匹配,则可以通过“:?”将组设置为非捕获组。

\b(?:a|the|one) reasons? (?:for|of)\b

如果您想要在组中获取单词,请使用捕获组:

\b(a|the|one) (reasons?) (for|of)\b

不需要,也不应该这样做。 - Antti Haapala -- Слава Україні
@AnttiHaapala 为什么不呢?在这种情况下,它将接受\bone\b \breason\b作为一个部分。https://regex101.com/r/gD4nI3/1 - Mazdak
1
你说过“你需要使用捕获组”。其实你不需要也不应该这样做,你应该使用非捕获组,因为它们可能会有更少的开销(而且在某些情况下,捕获组的行为是不同的)。 - Antti Haapala -- Слава Україні
@AnttiHaapala 是的,它们是不同的,但我建议使用捕获组来限制管道(OR)的行为! - Mazdak

5
正则表达式修饰符A|B表示“如果A或B匹配,则整个内容匹配”。因此,在您的情况下,生成的正则表达式在以下5个正则表达式中的任何一个匹配时都会匹配:
  • \ba\b
  • \bthe\b
  • \bone\b \breason\b
  • reasons\b \bfor\b
  • \bof\b

为了限制|的应用程度,请使用非捕获组合,即(?:something|something else)。另外,对于在reason末尾有可选的s,您不需要使用选择;这与reasons?完全相同。
因此,我们得到正则表达式\b(?:a|the|one) reasons? (?:for|of)\b
请注意,您不需要在正则表达式中使用单词边界运算符\b只需在开头和结尾使用(否则它将匹配类似于everyone reasons forever的内容)。

1
你对s?的理解是正确的,但问题是关于分组替代方案,而不是搜索reason|reasons - alexis
2
@alexis,你误解了。问题不是关于分组替代方案的。问题是如何编写正则表达式以匹配类似于一个/某个原因/原因的模式 - Antti Haapala -- Слава Україні

3

据我所理解,您想要类似于这样的正则表达式:

(?:a|the|one)\s+(?:reason|reasons)\s+(?:for|of)

非常简单,只需使用groups将它们组合起来。

请参见:DEMO

注意:您上面的要求对我来说并不是那么严格,如果您想自己修改一些内容,请考虑下面的说明。

说明

(?:abc|ijk|xyz)

任何一个由非捕获组(?:...)分组的单词abcijkxyz,都意味着这个单词不会被捕获到正则表达式变量$1$2$3...中。

\s+

这是单词分隔符,在这里我将其设置为任何空格,+表示1个或多个。


3
一个有趣的特性是 正则表达式模块 的命名列表。使用它,您不必在一个非捕获组中包含几个由 | 分隔的备选项。您只需要在模式之前定义列表,并通过名称引用它。例如:
import regex

words = [ ['a', 'the', 'one'], ['reason', 'reasons'], ['for', 'of'] ]

pattern = r'\m \L<word1> \s+ \L<word2> \s+ \L<word3> \M'
p = regex.compile(pattern, regex.X, word1=words[0], word2=words[1], word3=words[2])

s = 'the reasons for'

print(p.search(s))

即使这个功能不是必要的,但它可以提高可读性。

如果在加入项目前使用|,则可以使用re模块实现类似的效果:

import re

words = [ ['a', 'the', 'one'], ['reason', 'reasons'], ['for', 'of'] ]

words = ['|'.join(x) for x in words]

pattern = r'\b ({}) \s+ ({}) \s+ ({}) \b'.format(*words)

p = re.compile(pattern, re.X)

2
使用括号进行分组:
'\b(a|the|one) reason(|s) (for|of)\b'

我省略了句内的\b,因为空格已经暗示了它们:跟在字母后面的空格总是单词边界。通常你应该把\b放在选项之外;这样更短更易读。
如果有必要,您可以在所有现代正则表达式引擎中使用“非捕获组”:使用(?:stuff)而不是(stuff)。但如果对您的用途没有影响,或者您需要知道哪些单词选项实际上存在,则使用简单括号。

1

you can just use:

r"\b(a|the)\b"

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