qual è la differenza tra ?:, ?! e? = in regex?


107

Ho cercato il significato di queste espressioni ma non sono riuscito a capire l'esatta differenza tra loro. Questo è quello che dicono:

  • ?: Corrisponde all'espressione ma non acquisirla.
  • ?= Trova la corrispondenza con un suffisso ma escludilo dall'acquisizione.
  • ?! Corrisponde se il suffisso è assente.

Ho provato a usarli in semplice RegEx e ho ottenuto risultati simili per tutti. esempio: le 3 espressioni seguenti danno risultati molto simili.

  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?!\.[a-zA-Z0-9]+)*
  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?=\.[a-zA-Z0-9]+)*
  • [a-zA-Z0-9._-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9]+)*

Mostraci il tuo caso di test. Non dovrebbero dare gli stessi risultati.
Bergi

@ sepp2k, gli stessi risultati simili in pochi casi, uno dei quali menzionato nella domanda.
RK Poddar

@Bergi, l'ho testato con dati casuali, contenenti parole inglesi, numeri di telefono, URL, indirizzi e-mail, numeri, ecc.
RK Poddar

4
@ RKAgarwal Ah, vedo cosa hai fatto lì. Hai aggiunto un *dopo i gruppi, quindi sono semplicemente ignorati.
sepp2k

nota di noobie : li useresti solo all'inizio delle parentesi e le parentesi formano un gruppo di cattura (diversi set di parentesi estraggono diverse sezioni di testo).
Ryan Taylor

Risposte:


152

La differenza tra ?=e ?!è che il primo richiede che l'espressione data corrisponda e il secondo richiede che non corrisponda. Ad esempio a(?=b), corrisponderà alla "a" in "ab", ma non alla "a" in "ac". Considerando a(?!b)che corrisponderà la "a" in "ac", ma non la "a" in "ab".

La differenza tra ?:e ?=è che ?=esclude l'espressione dall'intera corrispondenza mentre ?:semplicemente non crea un gruppo di acquisizione. Quindi, ad esempio a(?:b), corrisponderà alla "ab" in "abc", mentre a(?=b)corrisponderà solo alla "a" in "abc". a(b)corrisponderebbe alla "ab" in "abc" e creerebbe un'acquisizione contenente la "b".


80
?:  is for non capturing group
?=  is for positive look ahead
?!  is for negative look ahead
?<= is for positive look behind
?<! is for negative look behind

Si prega di controllare qui: http://www.regular-expressions.info/lookaround.html per un ottimo tutorial ed esempi su lookahead nelle espressioni regolari.


15
Tuttavia JavaScript non sa guardare dietro.
Bergi

1
Questo è più completo per le espressioni regolari generali.
Yan Yang

/ (? <= ^ a) b / ha funzionato per me in javascript! Non sembra esserci alcun tutorial per guardare dietro in Javascript su Internet.
Y. Yoshii

Solo le versioni recenti dei browser hanno iniziato a supportare look behind in JS
anubhava

- anubhava Non conosco alternative a / (? <= ^ A) b / usando l'espressione regolare pura. Forse posso, ma dovrei fare affidamento sulle funzioni di callback.
Y. Yoshii

21

Per capire meglio, applichiamo le tre espressioni più un gruppo di cattura e analizziamo ogni comportamento.

  • () gruppo di cattura : la regex tra parentesi deve essere abbinata e la corrispondenza crea un gruppo di cattura
  • (?:) gruppo non di cattura : la regex tra parentesi deve essere abbinata ma non crea il gruppo di cattura
  • (?=) sguardo positivo al futuro - afferma che la regex deve essere abbinata
  • (?!) sguardo negativo al futuro - afferma che è impossibile abbinare la regex

Applichiamo q(u)iper smettere . qcorrisponde a q e il gruppo di acquisizione ucorrisponde a u . Viene presa la corrispondenza all'interno del gruppo di acquisizione e viene creato un gruppo di acquisizione. Quindi il motore continua i. E icorrisponderà a i . Quest'ultimo tentativo di incontro ha successo. qui è abbinato e un gruppo di cattura con u viene creato .

Applichiamo q(?:u)iper smettere . Ancora una volta, qcorrisponde a q e il gruppo che non cattura ucorrisponde a u . La corrispondenza dal gruppo non di acquisizione viene presa, ma il gruppo di acquisizione non viene creato. Quindi il motore continua i. E icorrisponderà a i . Quest'ultimo tentativo di incontro ha successo. qui è abbinato

Applichiamo q(?=u)iper smettere . Il lookahead è positivo ed è seguito da un altro token. Di nuovo, qcorrisponde a q e ucorrisponde a u . Anche in questo caso, la corrispondenza del lookahead deve essere scartata, quindi il motore torna indietro dalla istringa a u . Il lookahead ha avuto successo, quindi il motore continua i. Ma inon può corrispondere u . Quindi questo tentativo di incontro fallisce.

Applichiamo q(?=u)uper smettere . Il lookahead è positivo ed è seguito da un altro token. Di nuovo, qcorrisponde a q e ucorrisponde a u . La corrispondenza del lookahead deve essere scartata, quindi il motore torna indietro dalla ustringa a u . Il lookahead ha avuto successo, quindi il motore continua u. E ucorrisponderà u . Quindi questo tentativo di incontro ha successo. qu è abbinato

Applichiamo q(?!i)uper smettere . Anche in questo caso il lookahead è positivo (perché inon corrisponde) ed è seguito da un altro token. Di nuovo, qcorrisponde a q e inon corrisponde a u . La corrispondenza del lookahead deve essere scartata, quindi il motore torna indietro dalla ustringa a u . Il lookahead ha avuto successo, quindi il motore continua u. E ucorrisponderà u . Quindi questo tentativo di incontro ha successo. qu è abbinato

Quindi, in conclusione, la vera differenza tra i gruppi lookahead e non-capturing sta nel fatto che tu voglia solo testare l'esistenza o testare e salvare la corrispondenza. Il gruppo di cattura è costoso, quindi usalo con giudizio.


> quindi il motore torna indietro da i nella stringa a u. Il lookahead ha avuto successo, quindi il motore continua con i. Ma non riesco ad abbinarti QUESTO è totalmente confuso. Perché fare un passo indietro se questo è guardare avanti ?
Verde

1
@Green Una cosa importante da capire su lookahead e altri costrutti di lookaround è che, sebbene eseguano i movimenti per vedere se la loro sottoespressione è in grado di corrispondere, in realtà non "consumano" alcun testo. Questo potrebbe
creare

7

Prova ad abbinare foobarquesti:

/foo(?=b)(.*)/
/foo(?!b)(.*)/

La prima regex corrisponderà e restituirà "bar" come prima sottocatch - (?=b)corrisponde alla "b", ma non la consuma, lasciandola per le parentesi successive.

La seconda regex NON corrisponderà, perché si aspetta che "foo" sia seguito da qualcosa di diverso da "b".

(?:...)ha esattamente lo stesso effetto di simple (...), ma non restituisce quella parte come sottocatch.


0

Il modo più semplice per comprendere le asserzioni è trattarle come il comando inserito in un'espressione regolare. Quando il motore esegue un'asserzione, verificherà immediatamente la condizione descritta dall'asserzione. Se il risultato è vero, continua a eseguire l'espressione regolare.


0

Questa è la vera differenza:

>>> re.match('a(?=b)bc', 'abc')
<Match...>
>>> re.match('a(?:b)c', 'abc')
<Match...>

# note:
>>> re.match('a(?=b)c', 'abc')
None

Se non ti interessa il contenuto dopo "?:" O "? =", "?:" E "? =" Sono la stessa cosa. Entrambi possono essere usati.

Ma se hai bisogno di quei contenuti per ulteriori processi (non solo per farli corrispondere all'intera cosa. In tal caso puoi semplicemente usare "a (b)") Devi usare "? =" Invece. Perché "?:" Andrà via.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.