Come funziona?
Dai un'occhiata alla teoria degli automi
In breve, ogni espressione regolare ha un automa finito equivalente e può essere compilata e ottimizzata in un automa finito. Gli algoritmi coinvolti possono essere trovati in molti libri compilatori. Questi algoritmi sono usati da programmi unix come awk e grep.
Tuttavia, la maggior parte dei linguaggi di programmazione moderni (Perl, Python, Ruby, Java (e linguaggi basati su JVM), C #) non usano questo approccio. Usano un approccio ricorsivo di backtracking, che compila un'espressione regolare in un albero o una sequenza di costrutti che rappresentano vari sottogruppi dell'espressione regolare. La maggior parte delle sintassi moderne di "espressione regolare" offrono riferimenti indietro che sono al di fuori del gruppo di linguaggi regolari (non hanno rappresentazione in automi finiti), che sono banalmente implementabili in un approccio ricorsivo al backtracking.
L'ottimizzazione di solito produce una macchina a stati più efficiente. Ad esempio: considera aaaab | aaaac | aaaad, un normale programmatore può ottenere l'implementazione della ricerca semplice ma meno efficiente (confrontando tre stringhe separatamente) in dieci minuti; ma rendendolo conto equivale a aaaa [bcd], una ricerca migliore può essere fatta cercando i primi quattro "a", quindi testando il quinto carattere contro [b, c, d]. Il processo di ottimizzazione è stato uno dei miei lavori di compilazione a casa molti anni fa, quindi presumo che lo sia anche nella maggior parte dei moderni motori di espressione regolare.
D'altra parte, le macchine a stati hanno qualche vantaggio quando accettano stringhe perché usano più spazio rispetto a una "banale implementazione". Si consideri un programma per non sfuggire alle virgolette sulle stringhe SQL, ovvero: 1) inizia e termina con virgolette singole; 2) le virgolette singole sfuggono a due virgolette singole consecutive. Quindi: input ['a' ''] dovrebbe produrre output [a ']. Con una macchina a stati, le virgolette singole consecutive sono gestite da due stati. Questi due stati hanno lo scopo di ricordare la cronologia di input in modo tale che ciascun carattere di input venga elaborato esattamente una sola volta, come illustrato di seguito:
...
S1->'->S2
S1->*->S1, output *, * can be any other character
S2->'->S1, output '
S2->*->END, end the current string
Quindi, secondo me, l'espressione regolare può essere più lenta in alcuni casi banali, ma di solito più veloce di un algoritmo di ricerca creato manualmente, dato che l'ottimizzazione non può essere eseguita in modo affidabile dall'uomo.
(Anche in casi banali come la ricerca di una stringa, un motore intelligente può riconoscere il singolo percorso nella mappa degli stati e ridurre quella parte a un semplice confronto di stringhe ed evitare la gestione degli stati.)
Un motore specifico da un framework / libreria può essere lento perché il motore fa molte altre cose di cui un programmatore di solito non ha bisogno. Esempio: la classe Regex in .NET crea un gruppo di oggetti tra cui Match, Gruppi e Catture.