2015-09-23 10 views
5

Sto cercando di approfondire le espressioni regolari e desidero associare una condizione a meno che nella sottostringa non venga trovata anche una sottostringa. So che posso usare due dichiarazioni grepl (come visto di seguito), ma sto volendo usare una singola espressione regolare per verificare questa condizione, perché sto spingendo la mia comprensione. Diciamo che voglio abbinare le parole "cane" e "uomo" usando "(dog.*man|man.*dog)" (taken from here) ma non se la stringa contiene la sottostringa "parcheggio". Ho pensato che potrei usare (*SKIP)(*FAIL) per negare il "park", ma questo non causa il fallimento della stringa (mostrato sotto).regex corrisponde alla sottostringa, a meno che un'altra sottostringa non corrisponda a

  • Come posso abbinare la logica di trovare "cane" & "l'uomo", ma non "parco" con 1 regex?
  • Cosa c'è di sbagliato nella mia comprensione di (*SKIP)(*FAIL)|?

Il codice:

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) 

risposta

6

È possibile utilizzare la soluzione ancorata look-ahead (che richiede in stile Perl regexp):

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

Ecco un IDEONE demo

  • ^ - ancorare il motivo a l'inizio della stringa
  • (?!.*park) - sicuro partita se park è presentare
  • (?=.*dog.*man|.*man.*dog) - sicuro se la partita man e dog sono assenti.

Un'altra versione (più scalabile) con 3 look-aheads:

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

Nizza stavo pensando per qualche motivo non è possibile utilizzare un quantificatore all'interno di un lokaround. –

+2

È possibile utilizzare un quantificatore all'interno di un look-ahead, ma non è possibile utilizzare un quantificatore all'interno di un look-behind PCRE.Un quantificatore può essere utilizzato in NET look-behind e solo un quantificatore limitante con valori min e max può essere utilizzato in Java look-behind Java a larghezza vincolata. –

3

stribizhev ha già answered this question come dovrebbe essere affrontata: con un lookahead negativo.

io contribuisco a questa domanda particolare:

Cosa c'è di sbagliato con la mia comprensione di (*SKIP)(*FAIL)?

(*SKIP) e (*FAIL) sono regex verbi di controllo.

  1. (*FAIL) o (*F)
    Questo è il più facile da capire. (*FAIL) è esattamente lo stesso di un lookahead negativo con un subpattern vuoto: (?!). Non appena il motore regex arriva a quel verbo nel pattern, costringe a tornare immediatamente indietro.
  2. (*SKIP) Quando il motore regex incontra prima questo verbo, non succede nulla, perché agisce solo quando è raggiunto il backtracking. Ma se c'è un errore successivo, e raggiunge (*SKIP) da destra a sinistra, il backtracking non può passare (*SKIP). Causa:

    • Un errore di corrispondenza.
    • La prossima partita non verrà tentata dal personaggio successivo. Invece, inizierà dalla posizione nel testo in cui si trovava il motore quando ha raggiunto (*SKIP).

    Questo è il motivo per cui questi due verbi di controllo sono di solito insieme come (*SKIP)(*FAIL)

Consideriamo la seguente example:

  • Motivo: .*park(*SKIP)(*FAIL)|.*dog
  • Oggetto: "That park has too many dogs"
  • Partite : " has too many dog"

Internals:

  1. primo tentativo.
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. Secondo tentativo.
    Normalmente, dovrebbe iniziare dal secondo carattere nell'oggetto. Tuttavia, (*SKIP) ha questo comportamento particolare. Il secondo tentativo di inizio:
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


Come posso abbinare la logica di trovare "cane" & "l'uomo", ma non "parco" con 1 regex?

Utilizzare la soluzione di stribizhev !! Cerca di evitare l'uso dei verbi di controllo per motivi di compatibilità, non sono implementati in tutti gli aromi regex. Ma se sei interessato a queste stranezze regex, c'è un altro verbo di controllo più potente: (*COMMIT). È simile a (*SKIP), che agisce solo durante il backtracking, tranne che fa fallire l'intera partita (non ci saranno altri tentativi). Per 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

Problemi correlati