正则表达式 - 查找所有不以特定前缀开头的匹配单词

24

如何构建一个正则表达式来查找所有以某个字符串结尾但不以另一个字符串开头的单词?

例如,在以下句子中查找所有以“friend”结尾但不以“girl”开头的单词:

“当他们请求与他们交朋友时,男朋友女朋友多了一个朋友。”

粗体字的单词应该匹配,单词“女朋友”不应该匹配。


5
对于正则表达式问题,建议提及您所使用的编程语言,因为在执行相同任务时,不同编程语言的正则表达式语法可能存在差异。 - mrk
5个回答

29

从我记忆中,你可以尝试以下方法:

\b             # word boundary - matches start of word
(?!girl)       # negative lookahead for literal 'girl'
\w*            # zero or more letters, numbers, or underscores
friend         # literal 'friend'
\b             # word boundary - matches end of word

更新

以下是另一种不太明显的方法,在任何现代正则表达式实现中都应该适用:

假设您想要提取在多个上下文中出现的模式,但只有在特定上下文中出现时才匹配,您可以使用一个选择,首先指定您不想要的内容,然后捕获您需要的内容。

因此,以您的示例为例,要提取所有单词,这些单词要么是以friend结尾,要么包含friend但不是girlfriend,您可以使用:

\b               # word boundary
(?:              # start of non-capture group 
  girlfriend     # literal (note 1)
|                # alternation
  (              # start of capture group #1 (note 2)
    \w*          # zero or more word chars [a-zA-Z_]
    friend       # literal 
  )              # end of capture group #1
)                # end of non-capture group
\b

注:

  1. 这是我们不希望捕获的内容。
  2. 这是我们希望捕获的内容。

可以描述为:

  • 对于所有单词
  • 首先,匹配'girlfriend'并且不捕获(丢弃)
  • 然后匹配任何一个以'friend'结尾或包含'friend'的单词,并捕获它

Javascript代码:

const target = 'A boyfriend and girlfriend gained a friend when they asked to befriend them';

const pattern = /\b(?:girlfriend|(\w*friend))\b/g;

let result = [];
let arr;

while((arr=pattern.exec(target)) !== null){
  if(arr[1]) {
    result.push(arr[1]);
  }
}

console.log(result);

运行时将会打印出以下内容:

[ 'boyfriend', 'friend', 'befriend' ]

请注意,虽然许多正则表达式的变体不支持后顾,但大多数都支持前瞻。我给出的答案适用于JavaScript,它不支持后顾。 - Rob Raisch
@BartKiers,确实如此,但在这种情况下,它的作用是限定单词的起始和结束。 - Rob Raisch
1
当然,我知道。但是您首先称其为“开始”,然后第二个称其为“结束”,这可能会让人们认为它们匹配不同位置上的不同内容。另外,“边界”在这里是(我的看法)更好的词,因为这样就清楚它是一个“零宽度”模式(它不匹配任何字符,只匹配一个位置)。但是,正如我所说的:这只是小小的挑剔。 - Bart Kiers
我已经为此在regex101上创建了一个示例 https://regex101.com/r/cE0dM7/1 - Arne H. Bitubekk
@RobRaisch 你知道regex101只是一个在线正则表达式测试工具,用于展示你的正则表达式如何工作吗?它只是你回答的补充,供人们验证其有效性。此外,考虑到这是对你回答的评论,“example of this”也意味着这是你的正则表达式。 - Arne H. Bitubekk
显示剩余2条评论

10

可能会有效:

\w*(?<!女孩)朋友

如果你想匹配像 befriended 或者 男朋友 这样的单词,你也可以尝试:

\w*(?<!女孩)朋友\w*

我不确定在所有的正则表达式版本中是否都支持 ?<!,但这个表达式在 Expersso 中可用(我相信它是.NET)。


这将始终匹配“friend”(以及组1内的空字符串)。 - Bart Kiers
@Bart Kiers:它总是匹配在哪里?当我在 Expresso 中测试时,它不匹配 "女朋友"。 - FrustratedWithFormsDesigner
正确,它不会将单词“朋友”与其前面的单词“女孩”匹配。我的意思是,在“男朋友”这个词中,只有“朋友”这个词被匹配。它总是只匹配“朋友”这个词。 - Bart Kiers
原帖中提到:“加粗的单词应该匹配”,这些单词是“男朋友”、“朋友”和“交朋友”(包括“男孩”和“交”)。但是,在我看来,没有真正的理由调整你的答案,因为正确的答案已经被发布了不止一次。 - Bart Kiers

7

试试这个:

/\b(?!girl)\w*friend\b/ig

请问你能解释一下这个符号表示什么吗? - Chris Maggiulli
Rob Raisch在被接受的答案中已经很好地解释了。 - morja

4
我修改了Rob Raisch的答案,用一个正则表达式来查找包含特定子字符串但不包含另一个特定子字符串的单词。
\b(?![\w_]*Unwanted[\w_]*)[\w_]*Desired[\w_]*\b

例如,\b(?![\w_]*mon[\w_]*)[\w_]*day[\w_]*\b 可以找到包含“day”(例如day,tuesday,daywalker)的每个单词,但不包括同时包含“mon”(例如monday)的单词。可能对某些人有用。

似乎无法处理非字母数字字符。例如,如果我想检索除以“@”开头的单词外的所有点“。”(例如:Hello. @some.thing is not working . --> 应该检测到“Hello”后面和句子结尾的点,但不应检测“@some.thing”中的点)。 - MikeL

0
在我的案例中,我需要从正则匹配结果中排除一些具有给定前缀的单词。
这些文本是查询字符串参数。
?=&sysNew=false&sysStart=true&sysOffset=4&Question=1

前缀是sys,我不知道哪些单词中有sys。

解决问题的关键在于单词边界\b

\b(?!sys)\w+\b

然后我将那部分添加到了用于查询字符串的更大正则表达式中。

(\b(?!sys)\w+\b)=(\w+)

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