2012-03-23 20 views
7

sto cercando di costruire un'espressione regolare un po 'come questo:Regex: negativo look-ahead tra due partite

[match-word] ... [exclude-specific-word] ... [match-word] 

Questo sembra di lavorare con un negativo look-ahead, ma sto correndo in un problema quando ho un caso come questo:

[match-word] ... [exclude-specific-word] ... [match-word] ... [excluded word appears again] 

voglio la frase di cui sopra per abbinare, ma il negativo look-ahead tra la prima e la seconda parola abbinato "cadute oltre", così la seconda parola è mai abbinato.

Diamo un'occhiata a un esempio pratico.

I wan't per abbinare ogni frase che ha la parola "i" e la parola "torta", ma non la parola "odio" tra queste due parole. Ho questi tre frasi:

i sure like eating pie, but i love donuts <- Want to match this 
i sure like eating pie, but i hate donuts <- Want to match this 
i sure hate eating pie, but i like donuts <- Don't want to match this 

ho questo regex:

^i(?!.*hate).*pie   - have removed the word boundaries for clarity, original is: ^i\b(?!.*\bhate\b).*\bpie\b 

che corrisponde al primo periodo, ma non la seconda, perché il negativo look-ahead analizza l'intera stringa.

C'è un modo per limitare l'anteprima negativa, in modo che sia soddisfatto se incontra la "torta" prima che incontri "odio"?

Nota: nel mio implementazione, ci possono essere altri termini che seguono questo regex (che è costruito dinamicamente da un motore di ricerca di grammatica), per esempio:

^i(?!.*hate).*pie.*donuts 

Attualmente sto usando JRegex, ma potrebbe probabilmente passare al JDK Regex se necessario

Aggiornamento: ho dimenticato di dire qualcosa nella mia domanda iniziale:

E 'possibile che il "costrutto negativo" esiste ulteriormente nel Sente nce, e voglio abbinare la frase se è possibile anche se il costrutto "negativo" esiste più in alto.

Per chiarire, un'occhiata a queste frasi:

i sure like eating pie, but i love donuts <- Want to match this 
i sure like eating pie, but i hate donuts <- Want to match this 
i sure hate eating pie, but i like donuts <- Don't want to match this 
i sure like eating pie, but i like donuts and i hate making pie <- Do want to match this 

risposta di Rob funziona perfettamente per questo vincolo in più, quindi sto accettando che uno.

risposta

2

Ad ogni carattere tra la vostra marcia e arresto parole, è necessario assicurarsi che non corrisponde a negativo o interrompere parola. Ti piace questa (dove ho incluso un po 'di spazio bianco per migliorare la leggibilità):

^i ((?!hate|pie) .)* pie 

Ecco un programma Python per verificare le cose.

import re 

test = [ ('i sure like eating pie, but i love donuts', True), 
     ('i sure like eating pie, but i hate donuts', True), 
     ('i sure hate eating pie, but i like donuts', False) ] 

rx = re.compile(r"^i ((?!hate|pie).)* pie", re.X) 

for t,v in test: 
    m = rx.match(t) 
    print t, "pass" if bool(m) == v else "fail" 
+4

spazio bianco nel regex non aiuta la leggibilità, la sua unica fonte di confusione – Aprillion

+1

@death spazio bianco è valida in una regex pitone, con il flag "verbose". Confondendo per te, utile per me ... abbiamo opinioni diverse. (È anche facile da modificare.) – rob

+0

quindi perché non hai usato lo spazio bianco nel tuo esempio python? – Aprillion

2

per abbinare senza C tra ...A...B...

test in python:

$ python 
>>> import re 
>>> re.match(r'.*A(?!.*C.*B).*B', 'C A x B C') 
<_sre.SRE_Match object at 0x94ab7c8> 

Così ottengo questo regex:

.*\bi\b(?!.*hate.*pie).*pie 
2

Questa regex dovrebbe funzionare per voi

^(?!i.*hate.*pie)i.*pie.*donuts 

Spiegazione

"^" +   // Assert position at the beginning of a line (at beginning of the string or after a line break character) 
"(?!" +  // Assert that it is impossible to match the regex below starting at this position (negative lookahead) 
    "i" +   // Match the character “i” literally 
    "." +   // Match any single character that is not a line break character 
     "*" +   // Between zero and unlimited times, as many times as possible, giving back as needed (greedy) 
    "hate" +  // Match the characters “hate” literally 
    "." +   // Match any single character that is not a line break character 
     "*" +   // Between zero and unlimited times, as many times as possible, giving back as needed (greedy) 
    "pie" +  // Match the characters “pie” literally 
")" + 
"i" +   // Match the character “i” literally 
"." +   // Match any single character that is not a line break character 
    "*" +   // Between zero and unlimited times, as many times as possible, giving back as needed (greedy) 
"pie" +  // Match the characters “pie” literally 
"." +   // Match any single character that is not a line break character 
    "*" +   // Between zero and unlimited times, as many times as possible, giving back as needed (greedy) 
"donuts"  // Match the characters “donuts” literally