正则表达式匹配包含特定单词的句子,如果包含另一个特定单词则放弃匹配

时间:2018-11-17 19:09:32

标签: regex

问题出在标题上。可能吗

例如,我要搜索两个单词:apple, orange和一个使整个句子错误的单词:box因此表达式应接受以下句子: One orange and one apple,但丢弃此orange and apple within a box

我已经考虑了一段时间,但找不到任何解决方案。

2 个答案:

答案 0 :(得分:1)

您可以使用积极的前瞻性来匹配包含appleorange这样的单词的字符串,

(?=.*(orange|apple))

,并且如果包含box这样的单词,则可以使用否定的前瞻放弃匹配,

(?!.*box)

因此正则表达式变成了这个

^(?=.*(orange|apple))(?!.*box).*$

Here is the demo for same

如果您可以提供使用的语言,我也应该可以提供示例代码。

编辑:

以防万一,如果您使用的是当今最热门的python语言(尽管我的主要语言是Java),下面是相同的示例代码,

import re
strArr = ['One orange and one apple','One apple','One orange','orange and apple within a box','One apple and box','One orange and box','This contains none of accepted words so it doesn\'t match']
for x in strArr:
    print (x + ' --> ', end="")
    print (bool(re.match('^(?=.*(orange|apple))(?!.*box).*$', x)))

答案 1 :(得分:1)

首先,可以使用negative lookahead来实现 。但是,它太昂贵而无法使用。这是您要完成一项家庭作业或解决您正在滥用的系统所施加的某种愚蠢限制的事情。

话虽如此,请考虑以下内容:

  

我想在字符串的任何地方找到“ orange”一词。

您通常可以通过执行以下操作来利用正则表达式搜索:

/orange/

但是您也可以通过在单词之前插入“ match any”模式来将搜索与字符串的开头联系起来

/^.*orange/

(请注意,目前两个示例都不需要橙色作为单词。“ storange”之类的词会匹配。保存以备后用。)

您可以用苹果做同样的事情,但是如何将它们绑在一起呢?

一种简单的方法(可以在很多引擎上运行但可能效果不佳)只是简单地阐明了两种可能性:

  

我想找到单词“ orange”,后跟任意数量的字符,后跟单词“ apple”,或单词“ apple”,后跟任意数量的字符,后跟单词“ orange”。

这是一个替代,在正则表达式中为|(竖线)。有时,您可能需要转义正则表达式引擎的竖线(基本与扩展)。在某些其他时间,您可能必须对命令行解析器进行转义。因此,根据您使用正则表达式的方式,您可能必须编写|\\\\|或两者之间的内容。

但是,子模式很简单:

/orange.*apple/
/apple.*orange/

因此,首先将它们替换为一个非捕获组(如果可能!请检查您的文档,如果需要,请使用捕获组。),如下所示:

/(orange.*apple|apple.*orange)/

然后在前面加上“以字符串开头的领带”:

/^.*(orange.*apple|apple.*orange)/

现在,您可以匹配同时包含两个单词的文本。

最后,您可以利用否定前瞻的功能来阻止单词“ box”。为此,请使用特殊的语法,该语法可能会有所不同,但可能与(?! ... )相近(在我们的例子中,...是“ box”)。

  

我不想再看“盒子”一词。

是正则表达式吗?

/(?!box)/

但是对于您而言,您想说:

  

我不想在以下文本中的任何地方看到“框”一词。

另一个“任意字符”特殊之处:

/(?!.*box)/

现在,如何在现有模式中使用它?先行(和“后向”)都是零宽度断言。这意味着它们可能会失败,因为它们是断言,但是它们消耗零个输入字符(零宽度)。因此,您要做的就是注意放置它们的位置,因为它们会在与之相对应的任何地方准确地声明它们。

对于这种情况,我认为您想在一开始就做出一个简单的断言:“单词框不出现”,然后继续进行其他匹配:

  

我想找到一个没有单词“ box”的行,但其中包含... apple ... orange等。

您可以通过将锚点放到起点之后的前瞻位置来实现:

/^(?!.*box).*(apple.*orange|orange.*apple)/

这等于

At start of string,
 - confirm "box" does not appear in the line
 - match any character any number of times,
 - then either
   - match "apple", 
   - followed by any chars, any number of times
   - then "orange"
 - or
   - match "orange"
   - followed by any chars, any number of times
   - then "apple"

还有其他几种方法可以解决此问题。但是您需要注意性能。当您进行前瞻时,您正在邀请对该字符串进行另一次扫描。因此,如果您的前瞻对象为*+,则可能会一遍又一遍地重新扫描相同的文本。这会使您放慢速度,这就是为什么我建议在开始时先行提前的原因。您要么成功一次,要么立即失败。

同样,在您的单词之前和之间的.*是一个潜在的问题。现代引擎通常足够聪明,可以解决这个问题,但是某些数据库引擎不是很聪明。请注意:请进行一些性能测试,并使用遗漏的单词和重复的单词(苹果...苹果...橙色,苹果...橙色...橙色)以确保性能还可以。 (在这种情况下,“ ...”表示200个随机单词。)

最后,考虑您希望单词成为 words 的程度。正则表达式中有一种特殊的语法,该语法可能不存在或因引擎而异。通常,单词边界断言的拼写为\b,就像\bapple\b一样,但是您可能必须写\yapple\y\mapple\M\<apple\> ,甚至[[:<:]]apple[[:>:]]。检查您的文档。

最后,请考虑当互斥的替代项时,使用积极先行是处理替代项的另一种方法。代替apple.*orange|orange.*apple的构造,您可以只在模式的开头使用两个正向超前表达式。这具有明确的性能含义,因为这两个表达式表示对文本进行两次扫描。它确实简化了正则表达式的构造,如果您需要两个以上的单词,尤其是要以编程方式生成模式,则可能会出现问题:

/^(?!.*box)(?=.*apple)(?=.*orange)./

最后的.只是强制单个角色参与。该表达式表示

  

我想要一个不包含单词“ box”,不包含“ apple”和不包含“ orange”的行。

您可以看到如何用更多的单词来扩展它,但是请注意,每次执行?=.*时,您都在重新扫描文本。如果您的文字项目是80个字符或更少,那么您可能并不在意,但是,如果您要搜索成千上万个字符以寻找可能仅相隔几个字符的单词,那么以前的版本会更好。