使用正则表达式匹配任意顺序的多个单词

84
如题,我需要使用正则表达式在一个句子中找到两个特定单词。但这两个单词的顺序和大小写都可能不同。请问该如何处理?
比如说,无论单词 test 先出现还是 long 先出现,我都需要从以下句子中提取这两个单词:
This is a very long sentence used as a test

更新: 在第一部分中我没有提到的是,它也需要不区分大小写。


你在意单词的多次出现吗?你知道要提取哪些单词,还是想匹配符合特定模式的单词?你想找出它们所在的位置吗? - Dominic Rodger
我知道确切的单词,不关心多个单词,也不需要位置。但是我需要不区分大小写。 - RC1140
你正在使用哪种正则表达式引擎?JavaScript,.NET,PHP……?并且性能有多重要?你是否正在处理大量的字符串匹配?已经发布了几个可行的答案,但它们都不是特别高效的解决方案。 - Alan Moore
我认为今天我发现的最重要的事情是它在 .Net 中进行检查,所以我不确定下面所有的答案是否适用,我已经尝试了所有方法,可惜 .Net 不会将它们视为大小写不敏感。 - RC1140
2
嗯,大小写是否敏感不应该取决于正则表达式。最好编程软件不区分大小写。但是,为了使用正则表达式识别任意顺序的多个单词,我建议在正则表达式中使用量词(\b(james|jack)\b.*){2,}。与环视模式修改器不同,这适用于大多数正则表达式风格。 - XPMai
8个回答

60

8
这个失败了。https://regex101.com/r/Z8KOLp/2 - ChrisJJ
3
可以,但要求是找到/提取单词,而不仅仅是测试它们。 - ChrisJJ
6
另一个变体:(?is)^(?=.*\b(test)\b)(?=.*?\b(long)\b).*,它也捕获单词并匹配所有字符串。进一步锚定到^开头,可以显著提高性能。\b匹配单词边界 - bobble bubble
好的,对于我之前的评论,我想只使用\b(test)|\b(long)就足够了。 - Joniale
如果答案的正则表达式太慢,你可以像bobblebubble的回答中所说的那样,在其开头添加^来显著提高性能。 - Maarten
显示剩余3条评论

39

如果您想提取匹配项,请使用捕获组:(test)|(long) 然后,根据所使用的语言,您可以使用 $1 和 $2 引用匹配的组。


我将这个答案与下面的答案中的(?i)结合使用,这导致了以下输出(?i)(test(long)?),因为事实证明我必须先测试test然后再测试long。如果这是正确的方法则另说,但对我来说它起作用了。 - RC1140
2
假设要匹配test AND long,这个解决方案需要使用g标志。 - ChrisJJ

14

我假设(这总是很危险的)你想找到完整的单词,所以 "test" 会匹配而 "testy" 不会。因此,该模式必须搜索单词边界,所以我使用 "\b" 单词边界模式。

/(?i)(\btest\b.*\blong\b|\blong\b.*\btest\b)/

9

不知道是什么编程语言

 /test.*long/ 

或者

/long.*test/

或者
/test/ && /long/

2
我会添加单词边界,例如 /\btest\b/ - Helen

4

试试这个:

/(?i)(?:test.*long|long.*test)/

这将匹配testlong,或者longtest。它会忽略大小写的差异。


除非你想要它区分大小写,否则你不需要 (?i),但这样也可以。 - Rob

3

Vim有一个分支运算符\&,在搜索包含任意数量的单词且顺序不限的行时,可以使用更简洁的正则表达式。

例如:

/.*test\&.*long

将匹配包含testlong的行,顺序不限。

有关用法的更多信息,请参见此答案。 我不知道是否有其他正则表达式语言实现了分支运算符;该运算符甚至没有在正则表达式维基百科页面提到。


2

我使用C语言中的libpcre库,其中可以定义回调函数。这些函数帮助我轻松匹配不仅是单词,而且是任何顺序的子表达式。正则表达式如下:

(?C0)(expr1(?C1)|expr2(?C2)|...|exprn(?Cn)){n}

而callout函数则保证每个子表达式只匹配一次,例如:

int mycallout(pcre_callout_block *b){
static int subexpr[255];
if(b->callout_number == 0){
    //callout (?C0) - clear all counts to 0
    memset(&subexpr,'\0',sizeof(subexpr));
    return 0;
}else{
    //if returns >0, match fails
    return subexpr[b->callout_number-1]++;
}
}

类似的事情在Perl中也应该是可能的。

-9

我认为你不能用单个正则表达式完成它。你需要对两个正则表达式进行逻辑与操作,一个搜索每个单词。


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