Ciò che la maggior parte delle persone non riescono a prendere in considerazione quando si avvicina domande come questo è ciò che succede quando la regex non è in grado di trovare una corrispondenza. Questo è quando è probabile che le buche per le prestazioni dell'assassino siano visualizzate. Ad esempio, prendi l'esempio di Tim, dove stai cercando qualcosa come <tag>Hello!
. Considerate ciò che accade con:
<.*?>Hello!
Il motore regex trova una <
e trova rapidamente una chiusura >
, ma non >Hello!
. Quindi lo .*?
continua a cercare >
che è seguito da Hello!
.Se non ce n'è uno, andrà fino alla fine del documento prima che si arrenda. Quindi il motore regex riprende la scansione finché non trova un altro <
e ci riprova. sappiamo già come andrà a finire, ma il motore regex, in genere, non lo fa; passa attraverso lo stesso rigamarole con ogni <
nel documento. Consideriamo ora l'altra espressione regolare:
<[^>]*>Hello!
Come prima, corrisponde rapidamente dal <
al >
, ma non riesce a corrispondere Hello!
. Tornerà indietro allo <
, quindi uscirà e inizierà la scansione per un altro <
. Controllerà comunque ogni <
come la prima espressione regolare, ma non cercherà fino alla fine del documento ogni volta che ne trova una.
Ma è anche peggio di così. Se ci pensi, lo .*?
equivale effettivamente a un lookahead negativo. Sta dicendo "Prima di consumare il prossimo personaggio, assicurati che il resto della regex non possa corrispondere in questa posizione." In altre parole,
/<.*?>Hello!/
... è equivalente a:
/<(?:(?!>Hello!).)*(?:>Hello!|\z(*FAIL))/
Così in ogni posizione che si sta eseguendo, non solo un normale tentativo partita, ma un lookahead molto più costoso. (È ad almeno due volte più costosa, perché il lookahead deve esaminare almeno un carattere, quindi il .
va avanti e consuma un carattere.)
((*FAIL)
è uno dei backtracking-control verbs (supportati anche in PHP). |\z(*FAIL)
mezzi di Perl "o raggiungere la fine del documento e rinunciare".)
Infine, c'è un altro vantaggio dell'approccio di classe carattere negato. Anche se non lo fa (come @Bart sottolineato) agire come il quantificatore è possessivo, non c'è nulla che impedisca di fare è possessiva, se il sapore lo supporta:
/<[^>]*+>Hello!/
... o avvolgerlo in un gruppo atomica:
/(?><[^>]*>)Hello!/
non solo queste espressioni regolari mai marcia indietro inutilmente, non hanno per salvare le informazioni sullo stato che rende backtracking possibile.
Si noti che '[^>] *' sarà solo _non_ backtrack se è seguito da ciò che è all'interno della classe negata ('[^>] *>' in questo caso). Kobi, so che lo sai e probabilmente intendevi questo, ma volevo assicurarmi che gli altri non pensassero che '[^>] *' e '[^>] * +' (possessivo) siano gli stessi. Oltre a ciò, bella risposta! –
@Bart - Buon punto, questa è una scelta scadente di parole. Grazie! – Kobi