2015-09-23 9 views
5

Ich versuche tiefer in Regexes zu graben und eine Bedingung übereinstimmen, es sei denn, ein Teilstring ist auch in der gleichen Zeichenfolge gefunden. Ich weiß, dass ich zwei grepl Anweisungen verwenden kann (wie unten zu sehen), aber ich möchte eine einzige Regex verwenden, um diese Bedingung zu testen, da ich mein Verständnis vorantreiben möchte. Nehmen wir an, ich möchte die Wörter "Hund" und "Mann" mit "(dog.*man|man.*dog)" (taken from here) übereinstimmen, aber nicht, wenn die Zeichenfolge die Teilzeichenfolge "Park" enthält. Ich dachte, ich könnte (*SKIP)(*FAIL) verwenden, um den "Park" zu negieren, aber dies verursacht nicht die Zeichenfolge fehlgeschlagen (siehe unten).Regex Match Teilstring, es sei denn, ein anderer Teilstring passt

  • Wie kann ich die Logik entsprechen von finden "dog" & "Mann", aber nicht "Park" mit 1 regex?
  • Was ist falsch mit meinem Verständnis von (*SKIP)(*FAIL)|?

Der Code:

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) 

Antwort

6

Sie können die Anker Vorgriffs Lösung verwenden (erfordert Perl-Stil regexp):

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

Hier ist ein IDEONE demo

  • ^ - verankert das Muster an der Beginn der Zeichenfolge
  • (?!.*park) - nicht das Spiel, wenn park vorhanden ist
  • (?=.*dog.*man|.*man.*dog) - nicht das Spiel, wenn man und dog fehlen.

Eine andere Version (skalierbarer) mit 3 Look-aheads:

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

Nizza ich aus irgendeinem Grund dachte man keinen Quantor in einem lokaround verwenden können. –

+2

Sie können einen Quantifizierer in einem Look-Ahead verwenden, aber Sie können keinen Quantifizierer in einem PCRE-Look-Behind verwenden.Ein Quantifizierer kann in NET-Look-Behind verwendet werden, und nur ein begrenzender Quantifizierer mit Min- und Max-Werten kann in Javas eingeschränkter Java-Lookback verwendet werden. –

3

stribizhev hat bereits answered this question wie sie angegangen werden sollte: mit einem negativen Look-Ahead.

ich auf diese spezielle Frage beitragen werden:

Was ist mit meinem Verständnis von (*SKIP)(*FAIL) falsch ist?

(*SKIP) und (*FAIL) sind regex Steuer Verben.

  1. (*FAIL) oder (*F)
    Dies ist am einfachsten zu verstehen. (*FAIL) ist genau das gleiche wie ein negativer Lookahead mit einem leeren Untermuster: (?!). Sobald die Regex-Engine im Muster zu diesem Verb gelangt, erzwingt sie eine sofortige Rückverfolgung.
  2. (*SKIP) Wenn die Regex-Engine erste Verb trifft, passiert nichts, weil es nur wirkt, wenn es auf Rückzieher erreicht wird. Aber wenn es einen späteren Fehler gibt, und es (*SKIP) von rechts nach links erreicht, kann das Backtracking (*SKIP) nicht passieren. Es verursacht:

    • Ein Spielfehler.
    • Die nächste Übereinstimmung wird vom nächsten Zeichen nicht versucht. Stattdessen beginnt es an der Position im Text, wo der Motor war, als er (*SKIP) erreichte.

    Deshalb sind diese beiden Steuer Verben sind in der Regel zusammen als (*SKIP)(*FAIL)

Lassen Sie uns die example Folgendes berücksichtigen:

  • Muster: .*park(*SKIP)(*FAIL)|.*dog
  • Betreff: "That park has too many dogs"
  • Spiele : " has too many dog"

Internals:

  1. Erster Versuch.
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. Zweiter Versuch.
    Normalerweise sollte es mit dem zweiten Zeichen im Betreff beginnen. Jedoch hat (*SKIP) dieses besondere Verhalten. Der 2. Versuch beginnt:
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


Wie kann ich die Logik entsprechen der "Hund" & "Mann", aber nicht "Park" mit 1 regex finden?

Verwenden Sie stribizhevs Lösung !! Vermeiden Sie die Verwendung von Kontrollverben aus Kompatibilitätsgründen, da sie nicht in allen Regex-Varianten implementiert sind. Aber wenn Sie an diesen Regex-Merkwürdigkeiten interessiert sind, gibt es ein anderes stärkeres Kontrollverb: (*COMMIT). Es ist vergleichbar mit (*SKIP), das nur während des Backtracking funktioniert, außer dass das gesamte Match fehlschlägt (es wird überhaupt keinen weiteren Versuch geben). Für 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