L’espressione regolare su cui ricevo più feedback, per non parlare delle segnalazioni di “bug”, è quella che troverete proprio nella home page di questo sito: \b+@++.{2,}b. Questa espressione regolare, sostengo, corrisponde a qualsiasi indirizzo email. La maggior parte dei feedback che ricevo confutano questa affermazione mostrando un indirizzo email che questa espressione regolare non corrisponde. Di solito, il rapporto sul “bug” include anche un suggerimento per rendere la regex “perfetta”.
Come spiego di seguito, la mia affermazione è vera solo se si accetta la mia definizione di cosa sia realmente un indirizzo email valido, e cosa non lo sia. Se volete usare una definizione diversa, dovrete adattare la regex. La corrispondenza di un indirizzo email valido è un esempio perfetto che mostra che (1) prima di scrivere una regex, dovete sapere esattamente a cosa state cercando di corrispondere, e a cosa no; e (2) c’è spesso un compromesso tra ciò che è esatto e ciò che è pratico.
La virtù della mia espressione regolare sopra è che corrisponde al 99% degli indirizzi email in uso oggi. Tutti gli indirizzi e-mail che corrisponde possono essere gestiti dal 99% di tutti i software di posta elettronica là fuori. Se stai cercando una soluzione rapida, devi solo leggere il prossimo paragrafo. Se vuoi conoscere tutti i compromessi e avere un sacco di alternative tra cui scegliere, continua a leggere.
Se vuoi usare l’espressione regolare di cui sopra, ci sono due cose che devi capire. Primo, le regex lunghe rendono difficile formattare bene i paragrafi. Quindi non ho incluso la a-z in nessuna delle tre classi di caratteri. Questa regex è destinata ad essere usata con l’opzione “case insensitive” del vostro motore regex attivata. In secondo luogo, la regex di cui sopra è delimitata da confini di parole, il che la rende adatta ad estrarre indirizzi email da file o blocchi di testo più grandi. Se volete controllare se l’utente ha digitato un indirizzo email valido, sostituite i confini di parola con ancore di inizio e fine stringa, come questa: ^+@+\.{2,}$.
Il paragrafo precedente si applica anche a tutti gli esempi seguenti. Potresti aver bisogno di cambiare i confini delle parole in ancore di inizio/fine stringa, o viceversa. E devi attivare l’opzione di corrispondenza insensibile alle maiuscole/minuscole.
Trade-Off nella convalida degli indirizzi e-mail
Prima che l’ICANN rendesse possibile a qualsiasi azienda ben finanziata di creare i propri domini di primo livello, i domini di primo livello più lunghi erano i raramente usati .museum e .travel che sono lunghi 6 lettere. I domini di primo livello più comuni erano di 2 lettere per i domini specifici di un paese, e di 3 o 4 lettere per i domini di uso generale come .com e .info. Molte regex per la convalida degli indirizzi email che troverete in vari tutorial e riferimenti regex presuppongono ancora che il dominio di primo livello sia abbastanza corto. Le vecchie edizioni di questo tutorial regex menzionavano \b+@+\.{2,4}\b come la regex per gli indirizzi email nella sua introduzione. C’è solo una piccola differenza tra questa regex e quella in cima a questa pagina. Il 4 alla fine della regex limita il dominio di primo livello a 4 caratteri. Se usi questa regex con ancore per convalidare l’indirizzo email inserito nel tuo modulo d’ordine, [email protected] deve fare i suoi acquisti altrove. Sì, il TLD .solutions esiste e quando scrivo questo, disaproved.solutions può essere tuo per $16,88 all’anno.
Se vuoi essere più rigoroso di {2,} per il dominio di primo livello, ^+@+\.{2,63}$ è il massimo che puoi praticamente fare. Ogni parte di un nome di dominio non può essere più lunga di 63 caratteri. Non ci sono domini di primo livello a una cifra e nessuno contiene cifre. Non sembra che l’ICANN approverà nemmeno tali domini.
Gli indirizzi e-mail possono essere su server in un sottodominio come in [email protected]. Tutte le regex di cui sopra corrispondono a questo indirizzo email, perché ho incluso un punto nella classe di caratteri dopo il simbolo @. Ma le regex di cui sopra corrispondono anche a john@aol…com che non è valido a causa dei punti consecutivi. Puoi escludere tali corrispondenze sostituendo +\. con (?:+\.)+ in qualsiasi delle regex di cui sopra. Ho rimosso il punto dalla classe di caratteri e invece ho ripetuto la classe di caratteri e il seguente punto letterale. Ad esempio, ^+@(?:+\.)+{2,}$ corrisponde a [email protected] ma non a john@aol…com.
Se volete evitare che il vostro sistema si blocchi su input arbitrariamente grandi, potete sostituire i quantificatori infiniti con quelli finiti. ^{1,64}@(?:{1,63}.){1,125}{2,63}$ tiene conto che la parte locale (prima della @) è limitata a 64 caratteri e che ogni parte del nome di dominio è limitata a 63 caratteri. Non c’è un limite diretto al numero di sottodomini. Ma la lunghezza massima di un indirizzo email che può essere gestito da SMTP è di 254 caratteri. Quindi con una parte locale di un solo carattere, un dominio di primo livello di due lettere e sottodomini di un solo carattere, 125 è il numero massimo di sottodomini.
La regex precedente non limita effettivamente gli indirizzi email a 254 caratteri. Se ogni parte è alla sua lunghezza massima, la regex può corrispondere a stringhe fino a 8129 caratteri di lunghezza. Puoi ridurre questo abbassando il numero di sottodomini consentiti da 125 a qualcosa di più realistico come 8. Non ho mai visto un indirizzo email con più di 4 sottodomini. Se volete far rispettare il limite di 254 caratteri, la soluzione migliore è quella di controllare la lunghezza della stringa in ingresso prima ancora di usare una regex. Anche se questo richiede qualche riga di codice procedurale, controllare la lunghezza di una stringa è quasi istantaneo. Se potete usare solo le regex, ^{6,254}$ può essere usato come primo passaggio per assicurarsi che la stringa non contenga caratteri non validi e non sia troppo corta o troppo lunga. Se hai bisogno di fare tutto con una sola espressione regolare, avrai bisogno di una espressione regolare che supporti il lookahead. L’espressione regolare ^(?={6,254}$){1,64}@(?:{1,63}\.){1,8}{2,63}$ usa un lookahead per controllare prima che la stringa non contenga caratteri non validi e non sia troppo corta o troppo lunga. Quando il lookahead ha successo, il resto della regex fa un secondo passaggio sulla stringa per controllare il corretto posizionamento del segno @ e dei punti.
Tutte queste regex permettono i caratteri ._%+- ovunque nella parte locale. Potete forzare la parte locale ad iniziare con una lettera usando ^{0,63} invece di ^{1,64} per la parte locale: ^{0,63}@(?:{1,63}\.){1,125}{2,63}$. Quando si usa il lookahead per controllare la lunghezza complessiva dell’indirizzo, il primo carattere può essere controllato nel lookahead. Non abbiamo bisogno di ripetere il controllo del carattere iniziale quando controlliamo la lunghezza della parte locale. Questa regex è troppo lunga per adattarsi alla larghezza della pagina, quindi attiviamo la modalità free-spacing:
^(?={5,253}$)
{1,64}@(?:{1,63}\.){1,8}{2,63}$
I nomi di dominio possono contenere trattini. Ma non possono iniziare o finire con un trattino. (?:{0,62})? corrisponde a un nome di dominio tra 1 e 63 caratteri che inizia e finisce con una lettera o una cifra. Il gruppo di non-cattura rende la parte centrale del dominio e la lettera o la cifra finale opzionali nel loro insieme per assicurare che permettiamo domini con un solo carattere e allo stesso tempo assicurare che i domini con due o più caratteri non finiscano con un trattino. La regex complessiva inizia a diventare abbastanza complicata:
^{0,63}@
(?:(?:{0,62})?\.){1,8}{2,63}$
I nomi dei domini non possono contenere trattini consecutivi. +(?:-+)* corrisponde a un nome di dominio che inizia e finisce con una lettera o una cifra e che contiene qualsiasi numero di trattini non consecutivi. Questo è il modo più efficiente. Questa regex non fa alcun backtracking per trovare un nome di dominio valido. Corrisponde a tutte le lettere e cifre all’inizio del nome di dominio. Se non ci sono trattini, il gruppo opzionale che segue fallisce immediatamente. Se ci sono trattini, il gruppo corrisponde ad ogni trattino seguito da tutte le lettere e cifre fino al trattino successivo o alla fine del nome di dominio. Non possiamo far rispettare la lunghezza massima quando i trattini devono essere accoppiati con una lettera o una cifra, ma le lettere e le cifre possono stare da sole. Ma possiamo usare la tecnica del lookahead che abbiamo usato per far rispettare la lunghezza complessiva dell’indirizzo email per far rispettare la lunghezza del nome di dominio mentre non permettiamo i trattini consecutivi: (?={1,63}\.)+(?:-+)*. Notate che il lookahead controlla anche il punto che deve apparire dopo il nome di dominio quando è pienamente qualificato in un indirizzo email. Questo è importante. Senza il controllo del punto, il lookahead accetterebbe nomi di dominio più lunghi. Poiché il lookahead non consuma il testo a cui corrisponde, il punto non è incluso nella corrispondenza complessiva di questa regex. Quando mettiamo questa regex nella regex complessiva per gli indirizzi email, il punto sarà abbinato come lo era nelle regex precedenti:
^{0,63}@
(?:(?={1,63}\.)+(?:-+)*\.){1,8}{2,63}$
Se includiamo il lookahead per controllare la lunghezza complessiva, la nostra regex fa due passaggi sulla parte locale, e tre passaggi sui nomi di dominio per validare tutto:
^(?={5,253}$){1,64}@
(?:(?={1,63}\.)+(?:-+)*\.){1,8}{2,63}$
Su un PC o server moderno questa regex funzionerà bene quando convalida un singolo indirizzo email di 254 caratteri. Rifiutare input più lunghi sarebbe anche più veloce perché la regex fallirà quando il lookahead fallisce durante il primo passaggio. Ma non raccomanderei di usare una regex complessa come questa per cercare indirizzi email in un grande archivio di documenti o corrispondenza. È meglio usare la semplice regex all’inizio di questa pagina per raccogliere rapidamente tutto ciò che sembra un indirizzo email. Deduplica i risultati e poi usa una regex più severa se vuoi filtrare ulteriormente gli indirizzi non validi.
E parlando di backtracking, nessuna delle regex in questa pagina fa alcun backtracking per trovare indirizzi email validi. Ma in particolare le ultime possono fare un bel po’ di backtracking su qualcosa che non è proprio un indirizzo email valido. Se la vostra regex supporta quantificatori possessivi, potete eliminare ogni backtracking rendendo tutti i quantificatori possessivi. Poiché non è necessario alcun backtracking per trovare le corrispondenze, fare questo non cambia ciò che viene abbinato da queste regex. Permette loro solo di fallire più velocemente quando l’input non è un indirizzo email valido. La nostra regex più semplice diventa quindi ^++@++\.{2,}+$ con un + extra dopo ogni quantificatore. Possiamo fare lo stesso con la nostra regex più complessa:
^(?={5,253}+$){1,64}+@
(?:(?={1,63}+\.)++(?:-++)*+\.){1,8}+{2,63}+$
Un importante compromesso in tutte queste regex è che permettono solo lettere inglesi, cifre e i simboli speciali più comunemente usati. La ragione principale è che non mi fido di tutti i miei software di posta elettronica per essere in grado di gestire molto altro. Anche se John.O’[email protected] è un indirizzo email sintatticamente valido, c’è il rischio che alcuni software interpretino male l’apostrofo come una citazione delimitante. Inserire ciecamente questo indirizzo email in una query SQL, per esempio, nel migliore dei casi farà fallire le stringhe delimitate da apici singoli e nel peggiore aprirà il vostro sito ad attacchi di SQL injection.
E naturalmente, sono già molti anni che i nomi di dominio possono includere caratteri non inglesi. Ma la maggior parte del software si attiene ancora ai 37 caratteri a cui sono abituati i programmatori occidentali. Il supporto di domini internazionalizzati apre un intero vaso di Pandora su come i caratteri non-ASCII dovrebbero essere codificati. Quindi se usate una qualsiasi delle regex di questa pagina, chiunque abbia un indirizzo @ทีเอชนิค.ไทย sarà sfortunato. Ma forse è significativo che http://ทีเอชนิค.ไทย reindirizzi semplicemente a http://thnic.co.th anche se sono nel business della vendita di domini .ไทย.
La conclusione è che per decidere quale espressione regolare usare, se si sta cercando di abbinare un indirizzo email o qualcos’altro che è vagamente definito, è necessario iniziare a considerare tutti i compromessi. Quanto è brutto far corrispondere qualcosa che non è valido? Quanto è brutto non far corrispondere qualcosa che è valido? Quanto può essere complessa la vostra espressione regolare? Quanto sarebbe costoso se doveste cambiare l’espressione regolare in seguito perché si è rivelata troppo ampia o troppo stretta? Risposte diverse a queste domande richiederanno una diversa espressione regolare come soluzione. La mia espressione regolare per le email fa quello che voglio io, ma potrebbe non fare quello che vuoi tu.
Le espressioni regolari non inviano email
Non esagerare nel cercare di eliminare indirizzi email non validi con la tua espressione regolare. La ragione è che non sapete veramente se un indirizzo è valido finché non provate ad inviare un’email ad esso. E anche questo potrebbe non essere sufficiente. Anche se l’email arriva in una casella di posta, questo non significa che qualcuno legga ancora quella casella. Se hai davvero bisogno di essere sicuro che un indirizzo email sia valido, dovrai inviare un’email ad esso che contiene un codice o un link per il destinatario per eseguire un secondo passo di autenticazione. E se state facendo questo, allora non ha molto senso usare una regex che può rifiutare indirizzi email validi.
Lo stesso principio si applica in molte situazioni. Quando si cerca di far corrispondere una data valida, è spesso più facile usare un po’ di aritmetica per controllare gli anni bisestili, piuttosto che cercare di farlo in una regex. Usate un’espressione regolare per trovare potenziali corrispondenze o controllare se l’input usa la sintassi corretta, e fate la validazione effettiva sulle potenziali corrispondenze restituite dall’espressione regolare. Le espressioni regolari sono uno strumento potente, ma non sono certo una panacea.
Lo standard ufficiale: RFC 5322
Forse ti stai chiedendo perché non c’è una regex “ufficiale” a prova di errore per abbinare gli indirizzi email. Beh, c’è una definizione ufficiale, ma è difficilmente infallibile.
Lo standard ufficiale è conosciuto come RFC 5322. Descrive la sintassi a cui devono aderire gli indirizzi email validi. Potete (ma non dovreste, continuate a leggere) implementarla con la seguente espressione regolare. RFC 5322 lascia la parte del nome di dominio aperta a scelte specifiche dell’implementazione che non funzionano su Internet oggi. La regex implementa la sintassi “preferita” da RFC 1035 che è una delle raccomandazioni in RFC 5322:
\A(?:+(?:\.+)*
| “(?:
| \\\)*”)
@ (?:(?((?:*))+(?:*)?
||2|??)\\.){3}
(?:25|2|??|*:
(?:
| \\\\)+)
\])\z
Questa regex ha due parti: la parte prima della @, e la parte dopo la @. Ci sono due alternative per la parte prima della @. La prima alternativa permette che sia composta da una serie di lettere, cifre e alcuni simboli, compresi uno o più punti. Tuttavia, i punti non possono apparire consecutivamente o all’inizio o alla fine dell’indirizzo e-mail. L’altra alternativa richiede che la parte prima della @ sia racchiusa tra doppi apici, permettendo qualsiasi stringa di caratteri ASCII tra gli apici. I caratteri di spazio bianco, le doppie virgolette e i backslash devono essere evasi con backslash.
Anche la parte dopo la @ ha due alternative. Può essere un nome di dominio completamente qualificato (ad esempio regular-expressions.info), o può essere un indirizzo Internet letterale tra parentesi quadre. L’indirizzo Internet letterale può essere un indirizzo IP o un indirizzo di routing specifico del dominio.
La ragione per cui non dovresti usare questa regex è che è troppo ampia. La tua applicazione potrebbe non essere in grado di gestire tutti gli indirizzi email che questa regex permette. Gli indirizzi di routing specifici del dominio possono contenere caratteri di controllo ASCII non stampabili, il che può causare problemi se la tua applicazione ha bisogno di visualizzare gli indirizzi. Non tutte le applicazioni supportano la sintassi per la parte locale usando doppi apici o parentesi quadre. Infatti, la stessa RFC 5322 contrassegna la notazione con le parentesi quadre come obsoleta.
Abbiamo un’implementazione più pratica della RFC 5322 se omettiamo gli indirizzi IP, gli indirizzi specifici del dominio, la sintassi con i doppi apici e le parentesi quadre. Corrisponderà ancora al 99.99% di tutti gli indirizzi email in uso oggi.
\A+(?:\.+)*@
(?:(?:*)?\.)+(?:*)?\z
Nessuna di queste regex impone limiti di lunghezza sull’indirizzo email complessivo o sulla parte locale o sui nomi di dominio. RFC 5322 non specifica alcun limite di lunghezza. Queste derivano da limitazioni in altri protocolli come il protocollo SMTP per l’effettivo invio di email. RFC 1035 afferma che i domini devono essere di 63 caratteri o meno, ma non lo include nelle sue specifiche di sintassi. La ragione è che un vero linguaggio regolare non può imporre un limite di lunghezza e non permettere trattini consecutivi allo stesso tempo. Ma le moderne regex non sono veramente regolari, quindi possiamo aggiungere controlli sui limiti di lunghezza usando il lookahead come abbiamo fatto prima:
\A(?={6,254}\z)
(?={1,64}@)
+(?:\\1,63})*
@ (?:(?={1,63}\z)(?:*)?\z.)+
(?={1,63}\z)(?:*)?\z
Quindi anche quando si seguono gli standard ufficiali, ci sono ancora dei compromessi da fare. Non copiate ciecamente le espressioni regolari dalle librerie online o dai forum di discussione. Testatele sempre sui vostri dati e con le vostre applicazioni.
Fate una donazione
Questo sito vi ha appena fatto risparmiare un viaggio in libreria? Fai una donazione per sostenere questo sito, e avrai una vita intera di accesso senza pubblicità a questo sito!