2015-09-23 43 views
5

我想深入挖掘正则表达式,并希望匹配条件,除非在同一个字符串中也找到一些子字符串。我知道我可以使用两个grepl语句(如下所示),但我希望使用单个正则表达式来测试这种情况,因为我正在推动我的理解。假设我想使用"(dog.*man|man.*dog)"taken from here)匹配单词“狗”和“男人”,但如果字符串包含子字符串“park”,则不会。我想我可以使用(*SKIP)(*FAIL)来否定“park”,但这不会导致字符串失败(如下所示)。正则表达式匹配子字符串,除非另一个子字符串匹配

  • 我如何才能找到“狗” &“人”与1个正则表达式的逻辑,但不是“公园”相匹配?
  • 我对(*SKIP)(*FAIL)|的理解有什么问题?

代码:

x <- c(
    "The dog and the man play in the park.", 
    "The man plays with the dog.", 
    "That is the man's hat.", 
    "Man I love that dog!", 
    "I'm dog tired", 
    "The dog park is no place for man.", 
    "Park next to this dog's man." 
) 

# Could do this but want one regex 
grepl("(dog.*man|man.*dog)", x, ignore.case=TRUE) & !grepl("park", x, ignore.case=TRUE) 

# Thought this would work, it does not 
grepl("park(*SKIP)(*FAIL)|(dog.*man|man.*dog)", x, ignore.case=TRUE, perl=TRUE) 

回答

6

可以使用固定前瞻的解决方案(需要Perl样式的正则表达式):

grepl("^(?!.*park)(?=.*dog.*man|.*man.*dog)", x, ignore.case=TRUE, perl=T) 

这里是一个IDEONE demo

  • ^ - 将图案固定在字符串
  • (?!.*park)的开始 - 失败的比赛,如果park存在
  • (?=.*dog.*man|.*man.*dog) - 失败的比赛,如果mandog是不存在的。

另一个版本(更具可扩展性)与3查找aheads:

^(?!.*park)(?=.*dog)(?=.*man) 
+0

尼斯我在想你因为某些原因不能使用lokaround内的量词。 –

+2

您可以在预测中使用量词,但不能在PCRE后面使用量词。一个量词可以用在,NET后顾之外,并且只有具有最小值和最大值的限制量词可用于Java的约束宽度Java后代。 –

3

stribizhev早已answered this question因为它要具体分析:带负前瞻。

我将有助于这一特定问题:

什么是错我的(*SKIP)(*FAIL)理解?

(*SKIP)(*FAIL)是正则表达式控制动词

  1. (*FAIL)(*F)
    这是最容易理解的。 (*FAIL),确切地说与具有空子模式的负向前视相同:(?!)。只要正则表达式引擎在该模式中使用该动词,就会强制立即返回原路。
  2. (*SKIP) 当正则表达式引擎首先遇到这个动词,什么都不会发生,因为它只有当它在回溯达到的作用。但是如果后来出现故障,从右到左达到(*SKIP),回溯不能通过(*SKIP)。它会导致:

    • 匹配失败。
    • 接下来的比赛不会从下一个角色尝试。相反,它将从发动机到达(*SKIP)时的文本位置开始。

    这就是为什么这两个控制动词通常是一起(*SKIP)(*FAIL)

让我们考虑以下example

  • 模式:.*park(*SKIP)(*FAIL)|.*dog
  • 主题:"That park has too many dogs"
  • 匹配:" has too many dog"

内幕:

  1. 第一次尝试。
That park has too many dogs    || .*park(*SKIP)(*FAIL)|.*dog 
      /\          /\ 
      (here) we have a match for park 
       the engine passes (*SKIP) -no action 
       it then encounters (*FAIL) -backtrack 
       Now it reaches (*SKIP) from the right -FAIL! 
  • 第二次尝试。
    通常,它应该从主题中的第二个字符开始。但是,(*SKIP)具有此特定行为。第2次尝试开始:
  • That park has too many dogs    || .*park(*SKIP)(*FAIL)|.*dog 
          /\              /\ 
          (here) 
          Now, there's no match for .*park 
          And off course it matches .*dog 
    
        That park has too many dogs    || .*park(*SKIP)(*FAIL)|.*dog 
          ^   ^          ----- 
          | (MATCH!) | 
          +---------------+ 
    

    DEMO


    我如何用正则表达式1找到 “狗” & “人” 的逻辑而不是 “公园” 相匹配?

    使用stribizhev的解决方案!尽量避免使用控制动词以达到兼容性的目的,但并未在所有正则表达式中实现。但如果你对这些正则表达式怪异感兴趣,还有另一个更强大的控制动词:(*COMMIT)。它与(*SKIP)类似,只在回溯时才起作用,除非它导致整个比赛失败(根本不会有任何其他尝试)。对于example

    +-----------------------------------------------+ 
    |Pattern:          | 
    |^.*park(*COMMIT)(*FAIL)|dog     | 
    +-------------------------------------+---------+ 
    |Subject        | Matches | 
    +-----------------------------------------------+ 
    |The dog and the man play in the park.| FALSE | 
    |Man I love that dog!     | TRUE | 
    |I'm dog tired      | TRUE | 
    |The dog park is no place for man. | FALSE | 
    |park next to this dog's man.   | FALSE | 
    +-------------------------------------+---------+ 
    

    IDEONE demo

    相关问题