Regex, abbreviazione di espressione regolare, viene spesso utilizzato nei linguaggi di programmazione per la corrispondenza modelli nelle stringhe, trova e sostituisci, convalida dell'input e riformattazione del testo. Imparare a utilizzare correttamente le espressioni regolari può rendere molto più semplice lavorare con il testo.
Sintassi Regex, spiegata
Regex ha la reputazione di avere sintassi orrenda, ma è molto più facile scrivere che leggere. Ad esempio, ecco una regex generale per un validatore di posta elettronica conforme a RFC 5322:
(?: [a-z0-9! # $% & amp; '* +/=? ^ _ `{|} ~ -] + (?: . [a-z0-9! # $% & amp;' * +/=? ^ _ `{|} ~ -] +) * |” (?: [ x01- x08 x0b x0c x0e- x1f x21 x23- x5b x5d- x7f] | \ [ x01- x09 x0b x0c x0e- x7f]) * “) @ (?: (?: [a-z0-9] (?: [a-z0-9 -] * [a -z0-9])? .) + [a-z0-9] (?: [a-z0-9 -] * [a-z0-9])? | [(? 🙁 ?: 25 [ 0-5] | 2 [0-4] [0-9] | [01]? [0-9] [0-9]?) .) {3} (?: 25 [0-5] | 2 [0-4] [0-9] | [01]? [0- 9] [0-9]? | [A-z0-9 -] * [a-z0-9]: (?: [ X01 – x08 x0b x0c x0e- x1f x21- x5a x53- x7f] | \ [ x01- x09 x0b x0c x0e- x7f]) +) ])
Se sembra che qualcuno abbia sbattuto la faccia contro la tastiera, non sei solo. Ma sotto il cofano, tutto questo casino è in realtà la programmazione di una macchina a stati finiti. Questa macchina funziona per ogni personaggio, sbuffando e abbinando in base alle regole che hai impostato. Molti strumenti online renderanno i diagrammi ferroviari, mostrando come funziona la tua macchina Regex. Ecco la stessa espressione regolare in forma visiva:
Ancora molto confuso, ma è molto più comprensibile. È una macchina con parti in movimento che hanno regole che definiscono il modo in cui tutto combacia. Puoi vedere come qualcuno ha assemblato questo; non è solo un grande globo di testo.
Primo: usa un debugger Regex
Prima di iniziare, a meno che la tua Regex non sia particolarmente breve o tu sia particolarmente competente , dovresti usare un debugger in linea durante la scrittura e il test. Rende la comprensione della sintassi molto più semplice. Consigliamo Regex101 e RegExr, che offrono entrambi test e riferimenti alla sintassi incorporati.
Come funziona Regex?
Per ora, concentriamoci su qualcosa di molto più semplice. Questo è un diagramma di Regulex per un Regex di corrispondenza e-mail molto breve (e sicuramente non conforme a RFC 5322):
Il motore Regex si avvia a sinistra e viaggia lungo le linee, facendo corrispondere i caratteri man mano che procede. Il gruppo # 1 corrisponde a qualsiasi carattere tranne un'interruzione di riga e continuerà a trovare una corrispondenza con i caratteri fino a quando il blocco successivo non trova una corrispondenza. In questo caso, si interrompe quando raggiunge un simbolo @, il che significa che il gruppo n. 1 acquisisce il nome dell'indirizzo e-mail e tutto ciò che segue corrisponde al dominio.
L'espressione regolare che definisce il gruppo n. 1 nel nostro esempio di posta elettronica è:
(. +)
Le parentesi definiscono un gruppo di cattura, che dice al motore Regex di includere il contenuto della corrispondenza di questo gruppo in una variabile speciale. Quando esegui una Regex su una stringa, il valore di ritorno predefinito è l'intera corrispondenza (in questo caso, l'intera email). Ma restituisce anche ogni gruppo di cattura, il che rende questo Regex utile per estrarre i nomi dalle email.
Il punto è il simbolo di & # 8220; Qualsiasi carattere eccetto la nuova riga. & # 8221; Questo corrisponde a tutto su una riga, quindi se hai passato questa email Regex un indirizzo come:
% $ # ^ & amp;% * #% $ # ^ @ gmail.com
Corrisponderà a% $ # ^ & amp ;% * #% $ # ^ Come nome, anche se è ridicolo.
Il simbolo più (+) è una struttura di controllo che significa & # 8220; corrisponde al carattere o al gruppo precedente una o più volte. & # 8221; Garantisce la corrispondenza dell'intero nome e non solo del primo carattere. Questo è ciò che crea il loop che si trova nel diagramma ferroviario.
Il resto del Regex è abbastanza semplice da decifrare:
(. +) @ (. + .. +)
Il primo gruppo si ferma quando colpisce il simbolo @. Viene quindi avviato il gruppo successivo, che corrisponde nuovamente a più caratteri fino a raggiungere un carattere punto.
Poiché caratteri come punti, parentesi e barre vengono utilizzati come parte della sintassi in Regrex, ogni volta che si desidera abbinare quei caratteri è necessario eseguire l'escape correttamente con una barra rovesciata. In questo esempio, per abbinare il periodo scriviamo . e il parser lo considera come un simbolo che significa & # 8220; corrisponde a un punto. & # 8221;
Corrispondenza caratteri
Se hai caratteri non di controllo nella tua Regex, il motore Regex presumerà che quei caratteri formeranno un blocco corrispondente. Ad esempio, l'espressione regolare:
he + llo
corrisponderà alla parola & # 8220; hello & # 8221; con qualsiasi numero di e & # 8217; s. Qualsiasi altro carattere deve essere sottoposto a escape per funzionare correttamente.
Regex ha anche classi di caratteri, che fungono da scorciatoia per un set di caratteri. Questi possono variare in base all'implementazione Regex, ma questi sono standard:
- . & # 8211; corrisponde a qualsiasi cosa tranne la nuova riga.
- w & # 8211; corrisponde a qualsiasi & # 8220; parola & # 8221; carattere, comprese cifre e trattini bassi.
- d & # 8211; corrisponde ai numeri.
- b & # 8211; corrisponde ai caratteri di spazio (ad esempio, spazio, tabulazione, nuova riga).
Questi tre hanno tutti controparti maiuscole che invertono la loro funzione. Ad esempio, D corrisponde a tutto ciò che non è un numero.
Anche l'espressione regolare ha la corrispondenza del set di caratteri. Ad esempio:
[abc]
corrisponderà a a, b o c. Questo agisce come un blocco e le parentesi quadre sono solo strutture di controllo. In alternativa, puoi specificare un intervallo di caratteri:
[ac]
Oppure annulla l'insieme, che corrisponderà a qualsiasi carattere che non è nell'insieme:
[^ ac]
Quantificatori
I quantificatori sono una parte importante di Regex. Ti consentono di abbinare stringhe di cui non conosci il formato esatto, ma hai un'idea abbastanza buona.
L'operatore + dell'esempio di posta elettronica è un quantificatore, in particolare quello & # 8220; o più & # 8221; quantificatore. Se non sappiamo quanto è lunga una determinata stringa, ma sappiamo che è composta da caratteri alfanumerici (e non è vuota), possiamo scrivere:
w +
Oltre a +, ci sono anche:
- L'operatore *, che corrisponde a & # 8220; zero o più. & # 8221; Essenzialmente lo stesso di +, tranne per il fatto che ha la possibilità di non trovare una corrispondenza.
- Il? operatore, che corrisponde a & # 8220; zero o uno. & # 8221; Ha l'effetto di rendere un personaggio opzionale; o è lì o non è, e non corrisponde più di una volta.
- Quantificatori numerici. Questi possono essere un singolo numero come {3}, che significa & # 8220; esattamente 3 volte & # 8221; o un intervallo come {3-6}. Puoi omettere il secondo numero per renderlo illimitato. Ad esempio, {3,} significa & # 8220; 3 o più volte & # 8221 ;. Stranamente, non puoi tralasciare il primo numero, quindi se vuoi & # 8220; 3 o meno volte, & # 8221; dovrai utilizzare un intervallo.
Quantificatori avidi e pigri
Dietro le quinte, il Gli operatori * e + sono avidi. Corrisponde il più possibile e restituisce ciò che è necessario per iniziare il blocco successivo. Questo può essere un problema enorme.
Ecco un esempio: supponiamo che tu stia cercando di abbinare l'HTML o qualsiasi altra cosa con le parentesi graffe di chiusura. Il testo di input è:
& lt; div & gt; Hello World & lt;/div & gt;
E vuoi abbinare tutto ciò che è racchiuso tra parentesi. Puoi scrivere qualcosa come:
& lt;. * & Gt;
Questa è l'idea giusta, ma fallisce per un motivo fondamentale: il motore Regex corrisponde a & # 8220; div & gt; Hello World & lt;/div & gt; & # 8221; per la sequenza. *, quindi torna indietro fino a quando il blocco successivo corrisponde, in questo caso, a una parentesi di chiusura (& gt;). Ti aspetteresti che torni indietro per abbinare solo & # 8220; div & # 8220; e quindi ripeti di nuovo per abbinare il div di chiusura. Ma il backtracker viene eseguito dalla fine della stringa e si fermerà sulla parentesi finale, che finisce per corrispondere a tutto ciò che è all'interno delle parentesi.
La soluzione è rendere pigro il nostro quantificatore, il che significa che corrisponderà come il minor numero di caratteri possibile. Sotto il cofano, questo in realtà corrisponderà solo a un carattere, quindi si espanderà per riempire lo spazio fino alla corrispondenza del blocco successivo, il che lo rende molto più performante nelle operazioni Regex di grandi dimensioni.
Per rendere pigro un quantificatore si aggiunge un punto interrogativo subito dopo il quantificatore. Questo crea un po 'di confusione perché? è già un quantificatore (ed è effettivamente avido per impostazione predefinita). Per il nostro esempio HTML, il valore Regex viene corretto con questa semplice aggiunta:
& lt;. *? & Gt;
L'operatore pigro può essere applicato a qualsiasi quantificatore, incluso + ?, {0,3}? e persino ??. Anche se l'ultimo non ha alcun effetto; poiché in ogni caso stai facendo corrispondere zero o un carattere, non c'è spazio per l'espansione.
Raggruppamento e ricerca
< p>I gruppi in Regex hanno molti scopi. A un livello base, uniscono più gettoni in un unico blocco. Ad esempio, puoi creare un gruppo, quindi utilizzare un quantificatore sull'intero gruppo:
ba (na) +
Questo raggruppa i ripetuti & # 8220; na & # 8221; per abbinare la frase banana e banananana e così via. Senza il gruppo, il motore Regex corrisponderebbe ripetutamente al carattere finale.
Questo tipo di gruppo con due semplici parentesi è chiamato gruppo di cattura e lo includerà nell'output:
Se vuoi evitarlo, raggruppa semplicemente i token insieme per motivi di esecuzione, è possibile utilizzare un gruppo non di acquisizione:
ba (?: na)
Il punto interrogativo (un carattere riservato) definisce un gruppo non standard e il carattere seguente definisce il tipo di gruppo. Iniziare gruppi con un punto interrogativo è l'ideale, perché altrimenti se si desidera abbinare i punti e virgola in un gruppo, è necessario evitarli senza una buona ragione. Ma devi sempre sfuggire ai punti interrogativi in Regex.
Puoi anche nominare i tuoi gruppi, per comodità, quando lavori con l'output:
(? 'Group')
Puoi fare riferimento questi nel tuo Regex, il che li fa funzionare in modo simile alle variabili. Puoi fare riferimento a gruppi non denominati con il token 1, ma questo arriva solo fino a 7, dopodiché dovrai iniziare a denominare i gruppi. La sintassi per fare riferimento a gruppi denominati è:
k {group}
Questo fa riferimento ai risultati del gruppo denominato, che può essere dinamico. In sostanza, controlla se il gruppo si presenta più volte ma non si preoccupa della posizione. Ad esempio, questo può essere utilizzato per abbinare tutto il testo tra tre parole identiche:
La classe del gruppo è dove troverai la maggior parte della struttura di controllo di Regex, inclusi i lookahead. I lookahead assicurano che un'espressione deve corrispondere ma non la include nel risultato. In un certo senso, è simile a un'istruzione if e non corrisponderà se restituisce false.
La sintassi per un lookahead positivo è (? =). Ecco un esempio:
Questo corrisponde alla parte del nome di un indirizzo e-mail in modo molto chiaro, interrompendo l'esecuzione alla divisione @. I lookahead non consumano alcun carattere, quindi se vuoi continuare a correre dopo che un lookahead ha avuto successo, puoi comunque abbinare il carattere utilizzato nel lookahead.
Oltre ai lookahead positivi, ci sono anche:
- (?!) & # 8211; Lookahead negativi, che assicurano che un'espressione non corrisponda.
- (? & lt; =) & # 8211; Lookbehind positivi, che non sono supportati ovunque a causa di alcuni vincoli tecnici. Questi sono posti prima dell'espressione che vuoi trovare e devono avere una larghezza fissa (cioè nessun quantificatore eccetto {numero}. In questo esempio, potresti usare (? & Lt; = @) w + . W + per trovare la corrispondenza la parte del dominio dell'email.
- (? & lt ;!) & # 8211; Lookbehind negativi, che sono uguali ai lookbehind positivi, ma negati.
Differenze tra i motori Regex
Non tutti i motori Regex sono uguali. La maggior parte dei motori Regex non segue nessuno standard specifico e alcuni cambiano leggermente le cose per adattarle la loro lingua. Alcune funzioni che funzionano in una lingua potrebbero non funzionare in un'altra.
Ad esempio, le versioni di sed compilate per macOS e FreeBSD non supportano l'uso di t per rappresentare un carattere di tabulazione. Devi copiare manualmente un carattere di tabulazione e incollarlo nel terminale per utilizzare una tabulazione nella riga di comando sed.
La maggior parte di questo tutorial è compatibile con PCRE, il motore Regex predefinito utilizzato per PHP. Ma il motore Regex di JavaScript è diverso: non supporta i gruppi di cattura denominati tra virgolette (vuole le parentesi) e non può eseguire la ricorsione, tra le altre cose. Anche PCRE non è del tutto compatibile con versioni diverse e presenta molte differenze rispetto all'espressione regolare Perl.
Ci sono troppe differenze minori da elencare qui, quindi puoi usare questa tabella di riferimento per confrontare le differenze tra più motori Regex. Inoltre, i debugger Regex come Regex101 ti consentono di cambiare motore Regex, quindi assicurati di eseguire il debug utilizzando il motore corretto.
Come eseguire Regex
Abbiamo discusso della parte corrispondente delle espressioni regolari, che costituisce la maggior parte di ciò che rende Regex. Ma quando in realtà vuoi eseguire il tuo Regex, dovrai trasformarlo in un'espressione regolare completa.
Questo di solito ha il formato:
/match/g
Tutto dentro le barre in avanti è la nostra partita. La g è un modificatore di modalità. In questo caso, dice al motore di non smettere di funzionare dopo aver trovato la prima corrispondenza. Per trovare e sostituire Regex, dovrai spesso formattarlo come:
/trova/sostituisci/g
Questo sostituisce tutto in tutto il file. Puoi utilizzare i riferimenti di gruppo di acquisizione durante la sostituzione, il che rende Regex molto bravo nella formattazione del testo. Ad esempio, questa espressione regolare corrisponderà a qualsiasi tag HTML e sostituirà le parentesi standard con parentesi quadre:
/<(.+?)>/[1”/g
Quando viene eseguita, il motore match & lt; div & gt; e & lt;/div & gt ;, permettendoti di sostituire questo testo (e solo questo testo). Come puoi vedere, l'HTML interno non viene modificato:
Ciò rende Regex molto utile per trovare e sostituire il testo. L'utilità della riga di comando per eseguire questa operazione è sed, che utilizza il formato di base di:
sed '/find/replace/g' file & gt; file
Funziona su un file e restituisce STDOUT. Dovrai collegarlo a se stesso (come mostrato qui) per sostituire effettivamente il file sul disco.
Regex è anche supportato in molti editor di testo e può davvero velocizzare il tuo flusso di lavoro quando esegui batch operazioni. Vim, Atom e VS Code hanno tutti la funzione di ricerca e sostituzione di Regex incorporata.
Ovviamente, Regex può anche essere utilizzato in modo programmatico e di solito è integrato in molti linguaggi. L'esatta implementazione dipenderà dalla lingua, quindi dovrai consultare la documentazione della tua lingua.
Ad esempio, in JavaScript le espressioni regolari possono essere create letteralmente o dinamicamente utilizzando la RegExp globale oggetto:
var re = new RegExp ('abc')
Può essere usato direttamente chiamando il metodo .exec () dell'oggetto regex appena creato, o usando .replace (), .match () e .matchAll () sulle stringhe.