正则表达式匹配子字符串,除非另一个子字符串匹配

时间:2015-09-23 19:25:14

标签: regex r

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

  • 如何匹配find" dog"的逻辑? &安培; "人"但不是" 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)

2 个答案:

答案 0 :(得分:6)

您可以使用锚定的先行解决方案(需要Perl样式的正则表达式):

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

这是IDEONE demo

  • ^ - 将模式锚定在字符串的开头
  • (?!.*park) - 如果park存在,则匹配失败
  • (?=.*dog.*man|.*man.*dog) - 如果mandog不存在,则匹配失败。

另一个版本(更具可伸缩性),有3个预见:

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

答案 1 :(得分:3)

stribizhev已经answered this question,因为它应该接近:前瞻性负面。

我会为这个特殊问题做出贡献:

  

我对(*SKIP)(*FAIL)的理解有什么问题?

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

  1. (*FAIL)(*F)
    这是最容易理解的。 (*FAIL) 完全与具有空子模式的负前瞻相同:(?!)。一旦正则表达式引擎在模式中获得该动词,它就会立即强制回溯。
  2. <强> (*SKIP) 当正则表达式引擎首次遇到此动词时,没有任何反应,因为它仅在回溯时达到。但是如果稍后失败,并且它从右到左到达(*SKIP),则回溯无法通过(*SKIP)。它导致:

    • 比赛失败。
    • 下一场比赛将不会尝试下一场比赛。相反,它将从文本中引擎到达(*SKIP)时的位置开始。

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

  3. 让我们考虑以下example

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

    <强>塔内:

    1. 第一次尝试。
    2.     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!
      
      1. 第二次尝试。
        通常,它应该从主题中的第二个字符开始。但是,(*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

          

        我如何匹配find“dog”&amp;的逻辑? “男人”而不是“停放”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