Jak zweryfikować adres e-mail za pomocą wyrażenia regularnego?


3310

Z biegiem lat powoli opracowałem wyrażenie regularne, które poprawnie weryfikuje MOST adresy e-mail, zakładając, że nie używają adresu IP jako części serwera.

Używam go w kilku programach PHP i działa przez większość czasu. Jednak od czasu do czasu kontaktuję się z kimś, kto ma problem z witryną, która z niego korzysta, i ostatecznie muszę wprowadzić pewne poprawki (ostatnio zdałem sobie sprawę, że nie zezwalam na 4-znakowe domeny TLD).

Jakie jest najlepsze wyrażenie regularne, które znasz lub widziałeś do sprawdzania poprawności wiadomości e-mail?

Widziałem kilka rozwiązań wykorzystujących funkcje, które wykorzystują kilka krótszych wyrażeń, ale wolę mieć jedno długie złożone wyrażenie w prostej funkcji zamiast kilku krótkich wyrażeń w bardziej złożonej funkcji.



5
Wyrażenie regularne, które może sprawdzić, czy IDNA jest poprawnie sformatowane, nie pasuje do wymiany stosu. (zasady dotyczące kanonizacji zjadły naprawdę kręte i szczególnie nieprzystosowane do przetwarzania wyrażeń regularnych)
Jasen


Wyrażenia regularne mogą być zmienne, ponieważ w niektórych przypadkach wiadomość e-mail może zawierać spację, a w innych przypadkach nie może zawierać spacji.
Ṃųỻịgǻňạcểơửṩ

Odpowiedzi:


2439

Wyrażenie regularne w pełni zgodne z RFC 822 jest nieefektywne i niejasne ze względu na swoją długość. Na szczęście RFC 822 został dwukrotnie zastąpiony, a obecna specyfikacja adresów e-mail to RFC 5322 . RFC 5322 prowadzi do wyrażenia regularnego, który można zrozumieć, jeśli zostanie zbadany przez kilka minut i jest wystarczająco wydajny do faktycznego użycia.

Jedno wyrażenie regularne zgodne z RFC 5322 można znaleźć na górze strony pod adresem http://emailregex.com/, ale używa wzorca adresu IP, który unosi się w Internecie z błędem, który pozwala 00na dowolną z wartości dziesiętnych bajtu bez znaku w adres rozdzielany kropkami, co jest nielegalne. Reszta wydaje się być zgodna z gramatyką RFC 5322 i przechodzi kilka testów przy użyciu grep -Po, w tym nazw domen, adresów IP, złych adresów i nazw kont z cudzysłowami i bez nich.

Korygując 00błąd we wzorcu IP, otrzymujemy działający i dość szybki regex. (Złap renderowaną wersję, a nie obniżkę, dla rzeczywistego kodu.)

(?: [a-z0-9! # $% & '* + / =? ^ _ `{|} ~ -] + (?: \. [a-z0-9! # $% &' * + / =? ^ _ `{|} ~ -] +) * |" (?: [\ 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])? | \ [(? :(? :( 2 (5 [0-5] | [0-4] [0-9]) | 1 [0-9] [0-9] | [1-9]? [0-9])) \.) {3} ( ? :( 2 (5 [0-5] | [0-4] [0-9]) | 1 [0-9] [0-9] | [1-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]) +) \])

lub:

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\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])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-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])+)\])

Oto schemat z automatem skończonym do powyższego wyrażenia regularnego, które jest bardziej oczywiste niż sama regexp wprowadź opis zdjęcia tutaj

Bardziej wyrafinowane wzorce w Perlu i PCRE (biblioteka wyrażeń regularnych używanych np. W PHP) mogą poprawnie parsować RFC 5322 bez żadnych problemów . Python i C # też mogą to zrobić, ale używają innej składni niż te pierwsze dwa. Jeśli jednak musisz użyć jednego z wielu słabszych języków dopasowywania wzorców, najlepiej użyć prawdziwego analizatora składni.

Ważne jest również, aby zrozumieć, że sprawdzenie go zgodnie z RFC nie mówi absolutnie nic o tym, czy ten adres faktycznie istnieje w dostarczonej domenie, czy też osoba wpisująca adres jest jego prawdziwym właścicielem. Ludzie cały czas zapisują innych na listy mailingowe. Naprawianie wymagające bardziej wymyślnego sprawdzania poprawności, które polega na wysłaniu na ten adres wiadomości zawierającej token potwierdzający, który należy wprowadzić na tej samej stronie internetowej, co adres.

Tokeny potwierdzające to jedyny sposób, aby dowiedzieć się, czy masz adres osoby, która do niego wchodzi. Dlatego większość list mailingowych używa teraz tego mechanizmu do potwierdzania rejestracji. W końcu każdy może odłożyć president@whitehouse.gov, a to nawet będzie traktowane jako legalne, ale prawdopodobnie nie będzie to osoba na drugim końcu.

PHP, należy nie używać wzoru podanego w Weryfikuj adres e-mail z PHP, Right Way , z którego cytuję:

Istnieje pewne niebezpieczeństwo, że powszechne użycie i powszechne niechlujne kodowanie ustanowi de facto standard dla adresów e-mail, który będzie bardziej restrykcyjny niż zapisany standard formalny.

Nie jest to lepsze niż wszystkie inne wzorce inne niż RFC. Nie jest nawet wystarczająco inteligentny, aby obsługiwać nawet RFC 822 , a tym bardziej RFC 5322. Ten jednak jest.

Jeśli chcesz uzyskać fantazyjny i pedantyczny, zaimplementuj kompletny silnik stanowy . Wyrażenie regularne może działać tylko jako podstawowy filtr. Problem z wyrażeniami regularnymi polega na tym, że mówienie komuś, że jego idealnie poprawny adres e-mail jest nieprawidłowy (fałszywie dodatni), ponieważ twoje wyrażenie regularne nie może sobie z tym poradzić, jest po prostu niegrzeczne i niegrzeczne z punktu widzenia użytkownika. Mechanizm stanowy do tego celu może zarówno sprawdzać poprawność, a nawet poprawiać adresy e-mail, które w innym przypadku zostałyby uznane za nieprawidłowe, ponieważ dezasembluje adres e-mail zgodnie z każdym RFC. Pozwala to na potencjalnie bardziej przyjemne wrażenia, takie jak

Podany adres e-mail „myemail @ adres, com” jest nieprawidłowy. Miałeś na myśli „myemail@address.com”?

Zobacz także Sprawdzanie poprawności adresów e-mail , w tym komentarzy. Lub porównanie adresu e-mail sprawdzającego poprawność wyrażeń regularnych .

Wizualizacja wyrażeń regularnych

Demo Debuggex


179
Powiedziałeś „Nie ma dobrego wyrażenia regularnego”. Czy to jest ogólne czy specyficzne dla sprawdzania poprawności adresu e-mail?
Tomalak

37
@Tomalak: tylko dla adresów e-mail. Jak powiedział bortzmeyer, RFC jest niezwykle skomplikowane
Luk

37
Wspomniany artykuł w dzienniku linux jest pod wieloma względami błędny. W szczególności Lovell wyraźnie nie przeczytał erraty do RFC3696 i powtarza niektóre błędy w opublikowanej wersji RFC. Więcej tutaj: dominicsayers.com/isemail
Dominic Sayers

9
Jeff Atwood ma piękny regex w tym poście na blogu, aby sprawdzić poprawność wszystkich prawidłowych adresów e-mail: codinghorror.com/blog/2005/02/regex-use-vs-regex-abuse.html
CMircea

5
Zauważ, że obecna specyfikacja HTML5 zawiera wyrażenie regularne i ABNF do sprawdzania poprawności danych wejściowych typu e-mail, która jest celowo bardziej restrykcyjna niż oryginalne RFC.
Synchro

746

Nie należy używać wyrażeń regularnych do sprawdzania poprawności adresów e-mail.

Zamiast tego użyj klasy MailAddress , jak poniżej:

try {
    address = new MailAddress(address).Address;
} catch(FormatException) {
    // address is invalid
}

MailAddressKlasa wykorzystuje parser BNF, aby sprawdzić poprawność adresu w pełni zgodne z rfc822.

Jeśli planujesz użyć MailAddressadresu e-mail do sprawdzenia poprawności adresu e-mail, pamiętaj, że to podejście akceptuje również część nazwy wyświetlanej adresu e-mail i może to nie być dokładnie to, co chcesz osiągnąć. Na przykład akceptuje te ciągi jako prawidłowe adresy e-mail:

  • „użytkownik1@hotmail.com; użytkownik2@gmail.com”
  • „użytkownik1@hotmail.com; użytkownik2@gmail.com; użytkownik3@firma.com”
  • „Nazwa wyświetlana użytkownika user3@company.com”
  • „user4 @ company.com”

W niektórych przypadkach tylko ostatnia część ciągów jest analizowana jako adres; reszta przed tym jest nazwą wyświetlaną. Aby uzyskać zwykły adres e-mail bez nazwy wyświetlanej, możesz sprawdzić znormalizowany adres względem oryginalnego ciągu.

bool isValid = false;

try
{
    MailAddress address = new MailAddress(emailAddress);
    isValid = (address.Address == emailAddress);
    // or
    // isValid = string.IsNullOrEmpty(address.DisplayName);
}
catch (FormatException)
{
    // address is invalid
}

Ponadto adres z kropką na końcu, podobnie jak, user@company.jest również akceptowany przez MailAddress.

Jeśli naprawdę chcesz użyć wyrażenia regularnego, oto on :

(?: (?: \ r \ n)? [\ t]) * (?: (?: (?: [^ () <> @,;:: \\ ". \ [\] \ 000- \ 031 ] + (?: (?: (?: \ r \ n)? [\ t]
) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | "(?: [^ \" \ R \\] | \\. | (?: (?: \ r \ n)? [\ t])) * "(? :( ?:
\ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \ \ ". \ [\] \ 000- \ 031] + (? :(? :(
?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | "(?: [ ^ \ "\ r \\] | \\. | (?: (?: \ r \ n)? [ 
\ t])) * "(?: (?: \ r \ n)? [\ t]) *)) * @ (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 0
31] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\ ]])) | \ [([^ \ [\] \ r \\] | \\.) * \
] (?: (?: \ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] +
(?: (?: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]]) ) | \ [([^ \ [\] \ r \\] | \\.) * \] (?:
(?: \ r \ n)? [\ t]) *)) * | (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z
| (? = [\ ["() <> @,;: \\". \ [\]])) | "(?: [^ \" \ r \\] | \\. | (? :( ?: \ r \ n)? [\ t])) * "(?: (?: \ r \ n)
? [\ t]) *) * \ <(?: (?: \ r \ n)? [\ t]) * (?: @ (?: [^ () <> @,;: \\ "). \ [\] \ 000- \ 031] + (?: (?: (?: \
r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | \ [([^ \ [\ ] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [
 \ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)
? [\ t]) + | \ Z | (? = [\ ["() <> @;; \\". \ [\]])) | \ [([^ \ [\] \ r \ \] | \\.) * \] (?: (?: \ r \ n)? [\ t]
) *)) * (?:, @ (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [
 \ t]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *
) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031 ] + (?: (?: (?: \ r \ n)? [\ t]
) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | \ [([^ \ [\] \ R \\] | \\ .) * \] (?: (?: \ r \ n)? [\ t]) *)) *)
*: (?: (?: \ r \ n)? [\ t]) *)? (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) +
| \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | "(?: [^ \" \ R \\] | \\. | ( ?: (?: \ r \ n)? [\ t])) * "(?: (?: \ r
\ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ " . \ [\] \ 000- \ 031] + (? :(? :( ?:
\ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | "(?: [^ \ "\ r \\] | \\. | (?: (?: \ r \ n)? [\ t
])) * "(?: (?: \ r \ n)? [\ t]) *)) * @ (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031
] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\] ])) | \ [([^ \ [\] \ r \\] | \\.) * \] (
?: (?: \ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?
: (?: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (? :(?
: \ r \ n)? [\ t]) *)) * \> (?: (?: \ r \ n)? [\ t]) *) | (?: [^ () <> @ ,;; : \\ ". \ [\] \ 000- \ 031] + (? :(?
: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) ""? : [^ \ "\ r \\] | \\. | (?: (?: \ r \ n)?
[\ t])) * "(?: (?: \ r \ n)? [\ t]) *) *: (?: (?: \ r \ n)? [\ t]) * (?: (?: (?: [^ () <> @,;: \\ ". \ [\] 
\ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\" . \ [\]])) | "(?: [^ \" \ r \\] |
\\. | (?: (?: \ r \ n)? [\ t])) * "(?: (?: \ r \ n)? [\ t]) *) (?: \. (? : (?: \ r \ n)? [\ t]) * (?: [^ () <>

@,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ [ „() <> @,;: \\". \ [\]])) |
(?: [^ \ "\ r \\] | \\. | (?: (?: \ r \ n)? [\ t])) *" (?: (?: \ r \ n)? [ \ t]) *)) * @ (?: (?: \ r \ n)? [\ t]
) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\
". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) * ) (?: \. (?: (?: \ r \ n)? [\ t]) * (?
: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [
\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *)) * | (?: [^ () <> @,;: \\ ". \ [\] \ 000-
\ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [ \]])) | "(?: [^ \" \ r \\] | \\. | (
?: (?: \ r \ n)? [\ t])) * "(?: (?: \ r \ n)? [\ t]) *) * \ <(?: (?: \ r \ n)? [\ t]) * (?: @ (?: [^ () <> @ ,;)
: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ [" () <> @,;: \\ ". \ [\]])) | \ [([
^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ "
. \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @, ;: \\ ". \ [\]])) | \ [([^ \ [\
] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *)) * (?:, @ (?: (?: \ r \ n) )? [\ t]) * (?: [^ () <> @,;: \\ ". \
[\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\ ". \ [\]])) | \ [([^ \ [\] \
r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] 
\ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\" . \ [\]])) | \ [([^ \ [\] \ r \\]
| \\.) * \] (?: (?: \ r \ n)? [\ t]) *)) *) *: (?: (?: \ r \ n)? [\ t]) * )? (?: [^ () <> @,;: \\ ". \ [\] \ 0
00- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\"). \ [\]])) | "(?: [^ \" \ r \\] | \\
. | (?: (?: \ r \ n)? [\ t])) * "(?: (?: \ r \ n)? [\ t]) *) (?: \. (? :( ?: \ r \ n)? [\ t]) * (?: [^ () <> @,
;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ [" ( ) <> @,;: \\ ". \ [\]])) |" (?
: [^ \ "\ r \\] | \\. | (?: (?: \ r \ n)? [\ t])) *" (?: (?: \ r \ n)? [\ t ]) *)) * @ (?: (?: \ r \ n)? [\ t]) *
(?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\".
\ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *) ( ?: \. (?: (?: \ r \ n)? [\ t]) * (?: [
^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | ( ? = [\ ["() <> @,;: \\". \ [\]
])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *)) * \> ( ?: (?: \ r \ n)? [\ t]) *) (?:, \ s * (
?: (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\
". \ [\]])) |" (?: [^ \ "\ r \\] | \\. | (?: (?: \ r \ n)? [\ t])) *" (? : (?: \ r \ n)? [\ t]) *) (?: \. (? :(
?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (? :(? :(? : \ r \ n)? [\ t]) + | \ Z | (? = [
\ ["() <> @,;: \\". \ [\]])) | "(?: [^ \" \ r \\] | \\. | (?: (?: \ r \ n)? [\ t])) * "(?: (?: \ r \ n)? [\ t
]) *)) * @ (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)?] [\ T
]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | \ [([^ \ [\] \ R \\] | \ \.) * \] (?: (?: \ r \ n)? [\ t]) *) (?
: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + ( ?: (?: (?: \ r \ n)? [\ t]) + |
\ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | \ [([^ \ [\] \ R \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *)) * | (?:
[^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\
]])) | "(?: [^ \" \ r \\] | \\. | (?: (?: \ r \ n)? [\ t])) * "(?: (?: \ r \ n)? [\ t]) *) * \ <(?: (?: \ r \ n)
? [\ t]) * (?: @ (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r) \ n)? [\ t]) + | \ Z | (? = [\ ["
() <> @,;: \\ ". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)
? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <>

@,;: \\ ". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *)) * (?:, @ (?: (?: \ r \ n)?] [
 \ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,
;: \\ ". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]
) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\
". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) * )) *) *: (?: (?: \ r \ n)? [\ t]) *)?
(?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\".
\ [\]])) | "(?: [^ \" \ r \\] | \\. | (?: (?: \ r \ n)? [\ t])) * "(? :( ?: \ r \ n)? [\ t]) *) (?: \. (? :( ?:
\ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ [
"() <> @,;: \\". \ [\]])) | "(?: [^ \" \ r \\] | \\. | (?: (?: \ r \ n) ? [\ t])) * "(?: (?: \ r \ n)? [\ t])
*)) * @ (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t])
+ | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | \ [([^ \ [\] \ R \\] | \\. ) * \] (?: (?: \ r \ n)? [\ t]) *) (?: \
. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z
| (? = [\ ["() <> @,;: \\". \ [\]])) | \ [([^ \ [\] \ r \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *)) * \> (? :(
?: \ r \ n)? [\ t]) *)) *)?; \ s *)

26
Przekonasz się, że klasa MailAddress w .NET 4.0 jest znacznie lepsza w sprawdzaniu poprawności adresów e-mail niż w poprzednich wersjach. Wprowadziłem kilka istotnych ulepszeń.
Jeff Tucker,

7
Myślę, że to ... nie działa ... dla prostszych identyfikatorów. a @ b nie sprawdza poprawności. ar@b.com pasuje tylko do ar @ b, .com nie jest dopasowane. Jednak coś takiego jak „Jestem sobą” @ [10.10.10.10] działa! :)
Raze

5
Ostrzegamy, że te sprawdzające poprawność wyrażeń regularnych zgodne z RFC przepuszczą wiele adresów e-mail, których prawdopodobnie nie chciałbyś zaakceptować, takich jak „alert <body / onload = (' lol.com?'+document.cookies ) @aa> ”, który jest prawidłowym adresem e-mail w Perl's Email :: Valid (który korzysta z tego ogromnego wyrażenia regularnego), i może być wykorzystany do XSS rt.cpan.org/Public/Bug/Display.html?id=75650
Matthew Lock

9
@MatthewLock: To nie jest gorsze niż fake@not-a-real-domain.name. Nie można polegać na sprawdzaniu poprawności wiadomości e-mail, aby zapobiec XSS.
SLaks,

10
@MatthewLock: Nie. Musisz uciec zapytaniom SQL (lub, jeszcze lepiej, użyć parametrów). Odkażanie nie jest właściwą obroną.
SLaks,

536

To pytanie jest często zadawane, ale myślę, że powinieneś cofnąć się i zadać sobie pytanie, dlaczego chcesz zweryfikować składniowo adresy e-mail? Jaka jest tak naprawdę korzyść?

  • Nie będzie łapał typowych literówek.
  • Nie zapobiega to wprowadzaniu przez użytkowników nieprawidłowych lub wymyślonych adresów e-mail ani wprowadzaniu adresu innej osoby.

Jeśli chcesz sprawdzić poprawność wiadomości e-mail, nie masz innego wyboru, jak wysłać wiadomość e-mail z potwierdzeniem i poprosić użytkownika o odpowiedź. W wielu przypadkach będziesz musiał mimo to wysłać wiadomość potwierdzającą ze względów bezpieczeństwa lub ze względów etycznych (więc nie możesz np. Zapisać się do usługi wbrew jej woli).


92
Warto sprawdzić, czy wprowadzili coś @ coś w pole w walidacji po stronie klienta, aby wyłapać proste błędy - ale ogólnie masz rację.
Martin Beckett,

8
Martin, dałem ci +1, ale później przeczytałeś, że foobar @ dk to poprawny e-mail. Nie byłoby ładnie, ale jeśli chcesz być zarówno zgodny z RFC ORAZ kierować się zdrowym rozsądkiem, powinieneś wykryć takie przypadki i poprosić użytkownika o potwierdzenie, że jest poprawny.
philfreo

105
@olavk: jeśli ktoś wpisze literówkę (np .:) me@hotmail, to oczywiście nie dostanie e-maila z potwierdzeniem, a potem gdzie on jest? Nie ma ich już w Twojej witrynie i zastanawiają się, dlaczego nie mogli się zarejestrować. Właściwie nie, nie są - zupełnie o tobie zapomnieli. Jeśli jednak możesz po prostu wykonać podstawowe sprawdzenie poprawności za pomocą wyrażenia regularnego, gdy wciąż są one przy tobie, mogą od razu wykryć ten błąd i masz szczęśliwego użytkownika.
nickf

5
@JacquesB: Robisz doskonały punkt. To, że przekazuje skrót zgodnie z RFC, nie oznacza, że ​​tak naprawdę jest to adres tego użytkownika. W przeciwnym razie wszystkie te president@whitehouse.govadresy wskazują na bardzo zajętego wodza naczelnego. :)
tchrist

39
Nie musi być czarny ani biały. Jeśli wiadomość e-mail wygląda nieprawidłowo, poinformuj o tym użytkownika. Jeśli użytkownik nadal chce kontynuować, pozwól mu. Nie zmuszaj użytkownika do dostosowania się do wyrażenia regularnego, użyj wyrażenia regularnego jako narzędzia, aby pomóc użytkownikowi wiedzieć, że mógł wystąpić błąd.
ninjaneer

354

Wszystko zależy od tego, jak dokładny chcesz być. Do moich celów, gdzie staram się po prostu unikać takich rzeczy bob @ aol.com(spacje w wiadomościach e-mail) lub steve(w ogóle nie ma domeny) lub mary@aolcom(bez okresu przed .com), używam

/^\S+@\S+\.\S+$/

Jasne, pasuje do rzeczy, które nie są prawidłowymi adresami e-mail, ale jest to kwestia uzyskania typowych prostych błędów.

Istnieje wiele zmian, które można wprowadzić w tym wyrażeniu regularnym (niektóre z nich znajdują się w komentarzach do tej odpowiedzi), ale jest to proste i łatwe do zrozumienia i jest dobrą pierwszą próbą.


6
Nie pasuje do foobar @ dk, który jest prawidłowym i działającym adresem e-mail (chociaż prawdopodobnie większość serwerów pocztowych go nie zaakceptuje lub doda
coś.com

3
Tak, to będzie. Proponuję spróbować samemu. $ perl -le'print q{foo@bar.co.uk} = ~ /^\S+@\S+\.\S+$/? q {Y}: q {N} '
Andy Lester

7
@ Richard: .jest zawarty w \S.
David Thornley,

43
JJJ: Tak, będzie pasować do wielu gówna. Będzie pasować do & $ * # $ (@ $ 0 (%)) $ #.) & *) (* $. Dla mnie bardziej zależy mi na wyłapywaniu dziwnej literówki na palcach, jak na mary@aolcomcałkowitym śmieciu , YMMV
Andy Lester

5
Aby kontrolować @znaki: /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/ jsfiddle.net/b9chris/mXB96
Chris Moschini

338

To zależy od tego, co masz na myśli: Jeśli chodzi o przechwytywanie każdego prawidłowego adresu e-mail, użyj następujących elementów:

(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]
)+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:
\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(
?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ 
\t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\0
31]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\
](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+
(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:
(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z
|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)
?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\
r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[
 \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)
?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t]
)*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[
 \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*
)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]
)+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)
*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+
|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r
\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t
]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031
]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](
?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?
:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?
:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?
:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?
[ \t]))*"(?:(?:\r\n)?[ \t])*)*:(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] 
\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|
\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>
@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"
(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t]
)*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?
:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[
\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-
\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(
?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;
:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([
^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\"
.\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\
]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\
[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\
r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] 
\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]
|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \0
00-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\
.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,
;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?
:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*
(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".
\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[
^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]
]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)(?:,\s*(
?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(
?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[
\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t
])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t
])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?
:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|
\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:
[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\
]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)
?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["
()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)
?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>
@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[
 \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,
;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t]
)*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?
(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".
\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:
\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\[
"()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])
*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])
+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\
.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z
|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(
?:\r\n)?[ \t])*))*)?;\s*)

( http://www.ex-parrot.com/~pdw/Mail-RFC822-Address.html ) Jeśli szukasz czegoś prostszego, ale umożliwi to przechwycenie większości prawidłowych adresów e-mail, spróbuj czegoś takiego:

"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"

EDYCJA: Z linku:

To wyrażenie regularne będzie sprawdzać tylko adresy, które zostały pozbawione komentarzy i zastąpione białymi spacjami (odbywa się to przez moduł).


10
Nie pasuje do wszystkich adresów, niektóre muszą zostać najpierw przekształcone. Z linku: „To wyrażenie regularne będzie sprawdzać tylko adresy, które zostały pozbawione komentarzy i zastąpione białymi spacjami (robi to moduł)”.
Chas. Owens

47
Czy możesz podać mi przykład niektórych, email addressktóre błędnie przechodzą przez drugi, ale są łapane przez dłuższe wyrażenie regularne?
Lazer

4
Chociaż kiedyś to uwielbiałem, jest to walidator RFC 822, a nie RFC 5322 .
tchrist

24
@Lazer in..valid @ example.com byłby prostym przykładem. W części lokalnej nie możesz mieć dwóch kolejnych niecytowanych kropek.
Randal Schwartz,

5
@Mikhail perl, ale tak naprawdę nie powinieneś go używać.
Good Person

287

[ZAKTUALIZOWANO] Tutaj zebrałem wszystko, co wiem o sprawdzaniu poprawności adresu e-mail: http://isemail.info , który teraz nie tylko sprawdza poprawność, ale także diagnozuje problemy z adresami e-mail. Zgadzam się z wieloma komentarzami tutaj, że zatwierdzenie jest tylko częścią odpowiedzi; zobacz mój esej na http://isemail.info/about .

is_email () pozostaje, o ile wiem, jedynym walidatorem, który ostatecznie powie ci, czy dany ciąg jest prawidłowym adresem e-mail, czy nie. Przesłałem nową wersję na http://isemail.info/

Zebrałem przypadki testowe od Cal Henderson, Dave Child, Phil Haack, Doug Lovell, RFC5322 i RFC 3696. W sumie 275 adresów testowych. Przeprowadziłem wszystkie te testy dla wszystkich darmowych walidatorów, jakie mogłem znaleźć.

Postaram się aktualizować tę stronę, gdy ludzie ulepszają swoje moduły sprawdzające poprawność. Dziękuję Calowi, Michaelowi, Dave'owi, Paulowi i Philowi ​​za ich pomoc i współpracę przy opracowywaniu tych testów oraz konstruktywną krytykę mojego własnego weryfikatora .

Ludzie powinni być świadomi erraty w szczególności przeciwko RFC 3696 . Trzy z kanonicznych przykładów są w rzeczywistości nieprawidłowymi adresami. Maksymalna długość adresu to 254 lub 256 znaków, a nie 320.


Ten walidator również wydaje się poprawny. [... czas mija ...] Hm, wygląda na to, że to po prostu RFC 5322, a nie 3693 lub errata.
tchrist

1
Bardzo dobrze. Tutaj otrzymujemy nie tylko miły esej, ale także tester sprawdzania poprawności oraz bibliotekę do pobrania. Niezła odpowiedź!
bgmCoder

Twój walidator nie obsługuje kodu punycode (RFC 3492). nazwa@öäü.at może być prawidłowym adresem. (przekłada się na name@xn--4ca9at.at)
Josef mówi Przywróć Monikę

Cześć, Josef. Powinieneś spróbować sprawdzić poprawność, name@xn--4ca9at.atponieważ ten kod dotyczy sprawdzania poprawności, a nie interpretacji. Jeśli chcesz dodać tłumacz kodów kreskowych, z
Dominic Sayers

266

Zgodnie ze specyfikacją W3C HTML5 :

^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$

Kontekst:

Prawidłowy adres e-mail jest ciąg, który pasuje do produkcji ABNF [...].

Uwaga: Wymaganie to jest umyślne naruszenie z RFC 5322 , która definiuje składnię dla adresów e-mail, który jest jednocześnie zbyt surowe (przed znakiem „@”), zbyt ogólnikowe (po znaku „@”), a zbyt luźny ( umożliwianie praktycznego wykorzystania komentarzy, białych znaków i cytowanych ciągów w sposób nieznany większości użytkowników).

Poniższe wyrażenie regularne zgodne z JavaScript i Perl jest implementacją powyższej definicji.

/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/


12
To jest interesujące. To pogwałcenie RFC, ale jest to celowe i sprawia, że ​​jest spokojna. Przykład ze świata rzeczywistego: gmail ignoruje kropki w części przed @, więc jeśli twój e-mail to test@gmail.com, możesz wysłać e-maile na test. @ Gmail.com lub test .... @ gmail.com, oba te adresy są nieważne zgodnie z RFC, ale ważne w świecie rzeczywistym.
valentinas,

Myślę, że ostatnia część powinna być „+” zamiast „*”: ^ [a-zA-Z0-9.! # $% & '* + / =? ^ _ `{|} ~ -] + @ [a- zA-Z0-9 -] + (?: \. [a-zA-Z0-9 -] +) + $
mmmmmm

7
@mmmmmm john.doe@localhostjest poprawny. Na pewno w aplikacji w świecie rzeczywistym (tj. Społeczności) chciałbym, aby twoja sugestia zastąpiła * +
rabudde

3
@valentinas W rzeczywistości RFC nie wyklucza tych lokalnych części, ale należy je zacytować. "test...."@gmail.comjest całkowicie poprawny zgodnie z RFC i semantycznie równoważny test....@gmail.com.
Rinke,

Występuje błąd podczas próby wysłania wiadomości e-mail za pomocą Pythona przez przekaźnik mojej firmy, jeśli próbuję wysłać na adres z rozszerzeniem. @ Lub .. @. W rzeczywistości jest tak również w przypadku _ @. Wolę je usunąć przed wysłaniem, niż ufać, że odbiorca to zrobi.
ndvo

201

W Perlu 5.10 lub nowszym jest to łatwe:

/(?(DEFINE)
   (?<address>         (?&mailbox) | (?&group))
   (?<mailbox>         (?&name_addr) | (?&addr_spec))
   (?<name_addr>       (?&display_name)? (?&angle_addr))
   (?<angle_addr>      (?&CFWS)? < (?&addr_spec) > (?&CFWS)?)
   (?<group>           (?&display_name) : (?:(?&mailbox_list) | (?&CFWS))? ;
                                          (?&CFWS)?)
   (?<display_name>    (?&phrase))
   (?<mailbox_list>    (?&mailbox) (?: , (?&mailbox))*)

   (?<addr_spec>       (?&local_part) \@ (?&domain))
   (?<local_part>      (?&dot_atom) | (?&quoted_string))
   (?<domain>          (?&dot_atom) | (?&domain_literal))
   (?<domain_literal>  (?&CFWS)? \[ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
                                 \] (?&CFWS)?)
   (?<dcontent>        (?&dtext) | (?&quoted_pair))
   (?<dtext>           (?&NO_WS_CTL) | [\x21-\x5a\x5e-\x7e])

   (?<atext>           (?&ALPHA) | (?&DIGIT) | [!#\$%&'*+-/=?^_`{|}~])
   (?<atom>            (?&CFWS)? (?&atext)+ (?&CFWS)?)
   (?<dot_atom>        (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
   (?<dot_atom_text>   (?&atext)+ (?: \. (?&atext)+)*)

   (?<text>            [\x01-\x09\x0b\x0c\x0e-\x7f])
   (?<quoted_pair>     \\ (?&text))

   (?<qtext>           (?&NO_WS_CTL) | [\x21\x23-\x5b\x5d-\x7e])
   (?<qcontent>        (?&qtext) | (?&quoted_pair))
   (?<quoted_string>   (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
                        (?&FWS)? (?&DQUOTE) (?&CFWS)?)

   (?<word>            (?&atom) | (?&quoted_string))
   (?<phrase>          (?&word)+)

   # Folding white space
   (?<FWS>             (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
   (?<ctext>           (?&NO_WS_CTL) | [\x21-\x27\x2a-\x5b\x5d-\x7e])
   (?<ccontent>        (?&ctext) | (?&quoted_pair) | (?&comment))
   (?<comment>         \( (?: (?&FWS)? (?&ccontent))* (?&FWS)? \) )
   (?<CFWS>            (?: (?&FWS)? (?&comment))*
                       (?: (?:(?&FWS)? (?&comment)) | (?&FWS)))

   # No whitespace control
   (?<NO_WS_CTL>       [\x01-\x08\x0b\x0c\x0e-\x1f\x7f])

   (?<ALPHA>           [A-Za-z])
   (?<DIGIT>           [0-9])
   (?<CRLF>            \x0d \x0a)
   (?<DQUOTE>          ")
   (?<WSP>             [\x20\x09])
 )

 (?&address)/x

20
Chciałbym to zobaczyć w Pythonie
tdc

4
Myślę, że tylko podzbiór tej addrspecczęści jest naprawdę istotny dla pytania. Zaakceptowanie czegoś więcej i przekazanie go przez inną część systemu, która nie jest gotowa na przyjęcie pełnych adresów RFC5822, jest jak strzelanie własną stopą.
dolmen

3
Świetnie (+1), ale technicznie rzecz biorąc nie jest to regex oczywiście ... (co byłoby niemożliwe, ponieważ gramatyka nie jest regularna).
Rinke

10
Wyrażenia regularne przestały być normalne jakiś czas temu. Jest to jednak poprawne wyrażenie regularne w Perlu!
rjh

4
Ustawiłem test dla tego wyrażenia regularnego na IDEone: ideone.com/2XFecH Jednak nie jest to w porządku „idealnie”. Czy ktoś chciałby wejść do środka? Czy coś brakuje?
Mike

159

używam

^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$

Który jest używany w ASP.NET przez RegularExpressionValidator.


28
Gwizd! Mój (źle poinformowany) adres !@mydomain.netzostał odrzucony.
Phrogz

3
Według tej strony data.iana.org/TLD/tlds-alpha-by-domain.txt nie ma domen z pojedynczym znakiem na najwyższym poziomie, np. „ Coś.c ”, „ coś.a ”, oto wersja, która obsługuje co najmniej 2 znaki: „coś.pl”, „coś.us”:^\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w{2,}([-.]\\w+)*$
Tomasz Szulc

4
@Wayne Whitty. Dotarłeś do podstawowej kwestii, czy obsłużyć zdecydowaną większość adresów lub WSZYSTKIE, w tym adresy, których nikt by nie używał, z wyjątkiem testowania sprawdzania poprawności wiadomości e-mail.
Patanjali,

@TomaszSzulc dodatkowy ukośnik w odpowiedzi jest mylący, właśnie go poprawiłem i działa obsługa nazw domen 2 znaków, ^ \ w + ([- +. '] \ W +) * @ \ w + ([-.] \ W +) * \. \ w {2,} ([-.] \ w +) * $
Aqib Mumtaz

2
to się nie udaje, simon-@hotmail.comco w rzeczywistości jest ważne (nasz klient miał podobny adres) `
Simon_Weaver

142

Nie wiem najlepiej, ale ten jest co najmniej poprawny, pod warunkiem, że adresy zostały usunięte i zastąpione białymi spacjami.

Poważnie. Powinieneś użyć już napisanej biblioteki do sprawdzania poprawności wiadomości e-mail. Najlepszym sposobem jest prawdopodobnie wysłanie e-maila weryfikacyjnego na ten adres.


2
O ile mi wiadomo, niektóre biblioteki też się mylą. Niejasno pamiętam, że PHP PEAR miał taki błąd.
bortzmeyer

Ta strona zawiera również zastrzeżenie na dole dotyczące kilku rzeczy ze specyfikacji. którego wyrażenie regularne nie obsługuje.
Chris Vest

7
To specyfikacja RFC 822, a nie specyfikacja RFC 5322 .
tchrist

12
Ostatecznie ma rację, że jedynym sposobem na prawdziwą weryfikację adresu e-mail jest wysłanie na niego wiadomości e-mail i oczekiwanie na odpowiedź.
Blazemonger

109

Adresy e-mail, które chcę sprawdzić, będą używane przez aplikację sieci Web ASP.NET za pomocą przestrzeni nazw System.Net.Mail do wysyłania wiadomości e-mail do listy osób. Zamiast więc używać bardzo złożonego wyrażenia regularnego, po prostu próbuję utworzyć instancję MailAddress z adresu. Konstruktor MailAddress zgłosi wyjątek, jeśli adres nie zostanie poprawnie utworzony. W ten sposób wiem, że mogę przynajmniej wyciągnąć wiadomość e-mail z drzwi. Oczywiście jest to sprawdzanie poprawności po stronie serwera, ale i tak potrzebujesz tego przynajmniej.

protected void emailValidator_ServerValidate(object source, ServerValidateEventArgs args)
{
    try
    {
        var a = new MailAddress(txtEmail.Text);
    }
    catch (Exception ex)
    {
        args.IsValid = false;
        emailValidator.ErrorMessage = "email: " + ex.Message;
    }
}

3
Dobra uwaga. Nawet jeśli ta weryfikacja serwera odrzuci jakiś prawidłowy adres, nie stanowi to problemu, ponieważ i tak nie będzie można wysłać na ten adres przy użyciu tej konkretnej technologii serwera. Możesz też spróbować zrobić to samo, korzystając z dowolnej biblioteki e-mailowej innej firmy, której używasz zamiast domyślnych narzędzi.
Użytkownik

Naprawdę podoba mi się sposób, w jaki wykorzystuje to kod .Net - nie ma sensu wymyślać koła na nowo. To jest doskonałe. Prosty, czysty i zapewnia, że ​​możesz faktycznie wysłać wiadomość e-mail. Świetna robota.
Cory House

... tak, a dla osób zainteresowanych tym, jak się sprawdza, spójrz na kod w Reflector - jest go całkiem sporo - i nie jest to wyrażenie regularne!
Tom Carter,

2
Tylko uwaga: klasa MailAddress nie pasuje do RFC5322, jeśli chcesz po prostu użyć jej do sprawdzania poprawności (i nie wysyłać również, w którym to przypadku jest to kwestia sporna, jak wspomniano powyżej). Zobacz: stackoverflow.com/questions/6023589/...
Porges

Drobny problem: jeśli chcesz sprawić, by kod walidatora po stronie serwera był łatwiejszy do ponownego wykorzystania (w tym przypadku lub ogólnie), sugeruję użycie args.Valuezamiast odwoływania się do pola na txtEmail.Textsztywno. Ten ostatni powiąże twój walidator z pojedynczą instancją kontrolną, co może być OK, o ile masz jedno pole e-mail, ale nie jest zalecane inaczej.
pholpar

109

Szybka odpowiedź

Użyj następującego wyrażenia regularnego do sprawdzania poprawności danych wejściowych:

([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?(\.[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?)+

Adresy pasujące do tego wyrażenia regularnego:

  • mieć część lokalną (tj. część przed znakiem @), która jest ściśle zgodna z RFC 5321/5322,
  • mieć część domeny (tj. część po znaku @), która jest nazwą hosta z co najmniej dwiema etykietami, z których każda ma co najwyżej 63 znaki.

Drugim ograniczeniem jest ograniczenie RFC 5321/5322.

Opracuj odpowiedź

Użycie wyrażenia regularnego, które rozpoznaje adresy e-mail, może być przydatne w różnych sytuacjach: na przykład do skanowania adresów e-mail w dokumencie, do sprawdzania poprawności danych wprowadzanych przez użytkownika lub jako ograniczenia integralności repozytorium danych.

Należy jednak zauważyć, że jeśli chcesz dowiedzieć się, czy adres faktycznie odnosi się do istniejącej skrzynki pocztowej, nic nie zastąpi wysłania wiadomości na ten adres. Jeśli chcesz tylko sprawdzić, czy adres jest poprawny gramatycznie, możesz użyć wyrażenia regularnego, ale pamiętaj o tym""@[] jest to poprawny gramatycznie adres e-mail, który z pewnością nie odnosi się do istniejącej skrzynki pocztowej.

Składnia adresów e-mail została zdefiniowana w różnych dokumentach RFC , w szczególności w dokumentach RFC 822 i RFC 5322 . RFC 822 należy postrzegać jako „oryginalny” standard, a RFC 5322 jako najnowszy standard. Składnia zdefiniowana w RFC 822 jest najbardziej łagodna, a kolejne standardy jeszcze bardziej ją ograniczały, w których nowsze systemy lub usługi powinny rozpoznawać przestarzałą składnię, ale nigdy jej nie produkować.

W tej odpowiedzi wezmę „adres e-mail” w znaczeniu addr-speczdefiniowanym w RFC (tj. jdoe@example.orgAle nie "John Doe"<jdoe@example.org>, ani some-group:jdoe@example.org,mrx@exampel.org;).

Jest jeden problem z tłumaczeniem składni RFC na wyrażenia regularne: składnie nie są regularne! Wynika to z faktu, że pozwalają one na opcjonalne komentarze w adresach e-mail, które mogą być nieskończenie zagnieżdżone, podczas gdy nieskończonego zagnieżdżenia nie można opisać wyrażeniem regularnym. Aby wyszukać lub sprawdzić adresy zawierające komentarze, potrzebujesz analizatora składni lub bardziej zaawansowanych wyrażeń. (Zauważ, że języki takie jak Perl mają konstrukcje opisujące gramatykę bezkontekstową w sposób wyrażenia regularnego.) W tej odpowiedzi zignoruję komentarze i rozważę tylko właściwe wyrażenia regularne.

RFC definiują składnie wiadomości e-mail, a nie adresów jako takich. Adresy mogą pojawiać się w różnych polach nagłówka i tutaj są one przede wszystkim zdefiniowane. Gdy pojawiają się w polach nagłówka, adresy mogą zawierać (między tokenami leksykalnymi) spacje, komentarze, a nawet podziały wierszy. Semantycznie nie ma to jednak znaczenia. Usuwając ten biały znak itp. Z adresu, otrzymujesz semantycznie równoważną reprezentację kanoniczną . Zatem kanoniczna reprezentacja first. last (comment) @ [3.5.7.9]jest first.last@[3.5.7.9].

Do różnych celów należy stosować różne składnie. Jeśli chcesz skanować w poszukiwaniu adresów e-mail w (być może bardzo starym) dokumencie, dobrym pomysłem może być użycie składni zdefiniowanej w RFC 822. Z drugiej strony, jeśli chcesz zweryfikować dane wprowadzone przez użytkownika, możesz użyć składnia zgodnie z definicją w RFC 5322, prawdopodobnie przyjmując tylko reprezentacje kanoniczne. Powinieneś zdecydować, która składnia ma zastosowanie do konkretnego przypadku.

W tej odpowiedzi używam „rozszerzonych” wyrażeń regularnych POSIX, zakładając, że zestaw znaków jest zgodny z ASCII.

RFC 822

Doszedłem do następującego wyrażenia regularnego. Zapraszam wszystkich do spróbowania go złamać. Jeśli znajdziesz jakieś fałszywie pozytywne lub fałszywe negatywy, opublikuj je w komentarzu, a ja postaram się naprawić wyrażenie tak szybko, jak to możliwe.

([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]))*(\\\r)*")(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]))*(\\\r)*"))*@([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]))*(\\\r)*])(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]))*(\\\r)*]))*

Uważam, że jest w pełni zgodny z RFC 822, w tym z erratą . Rozpoznaje tylko adresy e-mail w formie kanonicznej. Dla wyrażenia regularnego, które rozpoznaje (składanie) białych znaków, zobacz pochodną poniżej.

Wyprowadzenie pokazuje, jak doszedłem do wyrażenia. Podaję wszystkie odpowiednie reguły gramatyczne z RFC dokładnie tak, jak się pojawiają, a następnie odpowiadające im wyrażenie regularne. Tam, gdzie opublikowano erratę, podaję osobne wyrażenie dla poprawionej reguły gramatyki (oznaczonej jako „erratum”) i używam zaktualizowanej wersji jako podwyrażenia w kolejnych wyrażeniach regularnych.

Jak stwierdzono w pkt 3.1.4. RFC 822 opcjonalną liniową spację można wstawić między tokeny leksykalne. W stosownych przypadkach rozszerzyłem wyrażenia, aby uwzględnić tę regułę i oznaczyłem wynik jako „opt-lwsp”.

CHAR        =  <any ASCII character>
            =~ .

CTL         =  <any ASCII control character and DEL>
            =~ [\x00-\x1F\x7F]

CR          =  <ASCII CR, carriage return>
            =~ \r

LF          =  <ASCII LF, linefeed>
            =~ \n

SPACE       =  <ASCII SP, space>
            =~  

HTAB        =  <ASCII HT, horizontal-tab>
            =~ \t

<">         =  <ASCII quote mark>
            =~ "

CRLF        =  CR LF
            =~ \r\n

LWSP-char   =  SPACE / HTAB
            =~ [ \t]

linear-white-space =  1*([CRLF] LWSP-char)
                   =~ ((\r\n)?[ \t])+

specials    =  "(" / ")" / "<" / ">" / "@" /  "," / ";" / ":" / "\" / <"> /  "." / "[" / "]"
            =~ [][()<>@,;:\\".]

quoted-pair =  "\" CHAR
            =~ \\.

qtext       =  <any CHAR excepting <">, "\" & CR, and including linear-white-space>
            =~ [^"\\\r]|((\r\n)?[ \t])+

dtext       =  <any CHAR excluding "[", "]", "\" & CR, & including linear-white-space>
            =~ [^][\\\r]|((\r\n)?[ \t])+

quoted-string  =  <"> *(qtext|quoted-pair) <">
               =~ "([^"\\\r]|((\r\n)?[ \t])|\\.)*"
(erratum)      =~ "(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*"

domain-literal =  "[" *(dtext|quoted-pair) "]"
               =~ \[([^][\\\r]|((\r\n)?[ \t])|\\.)*]
(erratum)      =~ \[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*]

atom        =  1*<any CHAR except specials, SPACE and CTLs>
            =~ [^][()<>@,;:\\". \x00-\x1F\x7F]+

word        =  atom / quoted-string
            =~ [^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*"

domain-ref  =  atom

sub-domain  =  domain-ref / domain-literal
            =~ [^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*]

local-part  =  word *("." word)
            =~ ([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*")(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*"))*
(opt-lwsp)  =~ ([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*")(((\r\n)?[ \t])*\.((\r\n)?[ \t])*([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*"))*

domain      =  sub-domain *("." sub-domain)
            =~ ([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*])(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*]))*
(opt-lwsp)  =~ ([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*])(((\r\n)?[ \t])*\.((\r\n)?[ \t])*([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*]))*

addr-spec   =  local-part "@" domain
            =~ ([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*")(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*"))*@([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*])(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*]))*
(opt-lwsp)  =~ ([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*")((\r\n)?[ \t])*(\.((\r\n)?[ \t])*([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*")((\r\n)?[ \t])*)*@((\r\n)?[ \t])*([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*])(((\r\n)?[ \t])*\.((\r\n)?[ \t])*([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]|(\r\n)?[ \t]))*(\\\r)*]))*
(canonical) =~ ([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]))*(\\\r)*")(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|"(\n|(\\\r)*([^"\\\r\n]|\\[^\r]))*(\\\r)*"))*@([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]))*(\\\r)*])(\.([^][()<>@,;:\\". \x00-\x1F\x7F]+|\[(\n|(\\\r)*([^][\\\r\n]|\\[^\r]))*(\\\r)*]))*

RFC 5322

Doszedłem do następującego wyrażenia regularnego. Zapraszam wszystkich do spróbowania go złamać. Jeśli znajdziesz jakieś fałszywie pozytywne lub fałszywe negatywy, opublikuj je w komentarzu, a ja postaram się naprawić wyrażenie tak szybko, jak to możliwe.

([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|\[[\t -Z^-~]*])

Uważam, że jest w pełni zgodny z RFC 5322, w tym z erratą . Rozpoznaje tylko adresy e-mail w formie kanonicznej. Dla wyrażenia regularnego, które rozpoznaje (składanie) białych znaków, zobacz pochodną poniżej.

Wyprowadzenie pokazuje, jak doszedłem do wyrażenia. Podaję wszystkie odpowiednie reguły gramatyczne z RFC dokładnie tak, jak się pojawiają, a następnie odpowiadające im wyrażenie regularne. Dla reguł, które zawierają semantycznie nieistotne (składane) białe spacje, podaję osobne wyrażenie regularne oznaczone „(znormalizowane)”, które nie akceptuje tej białej spacji.

Zignorowałem wszystkie zasady „obs-” z RFC. Oznacza to, że wyrażenia regularne pasują tylko do adresów e-mail ściśle zgodnych z RFC 5322. Jeśli musisz dopasować „stare” adresy (jak robi to luźniejsza gramatyka, w tym reguły „obs-”), możesz użyć jednego z wyrażeń regularnych RFC 822 z poprzedniego akapitu.

VCHAR           =   %x21-7E
                =~  [!-~]

ALPHA           =   %x41-5A / %x61-7A
                =~  [A-Za-z]

DIGIT           =   %x30-39
                =~  [0-9]

HTAB            =   %x09
                =~  \t

CR              =   %x0D
                =~  \r

LF              =   %x0A
                =~  \n

SP              =   %x20
                =~  

DQUOTE          =   %x22
                =~  "

CRLF            =   CR LF
                =~  \r\n

WSP             =   SP / HTAB
                =~  [\t ]

quoted-pair     =   "\" (VCHAR / WSP)
                =~  \\[\t -~]

FWS             =   ([*WSP CRLF] 1*WSP)
                =~  ([\t ]*\r\n)?[\t ]+

ctext           =   %d33-39 / %d42-91 / %d93-126
                =~  []!-'*-[^-~]

("comment" is left out in the regex)
ccontent        =   ctext / quoted-pair / comment
                =~  []!-'*-[^-~]|(\\[\t -~])

(not regular)
comment         =   "(" *([FWS] ccontent) [FWS] ")"

(is equivalent to FWS when leaving out comments)
CFWS            =   (1*([FWS] comment) [FWS]) / FWS
                =~  ([\t ]*\r\n)?[\t ]+

atext           =   ALPHA / DIGIT / "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "/" / "=" / "?" / "^" / "_" / "`" / "{" / "|" / "}" / "~"
                =~  [-!#-'*+/-9=?A-Z^-~]

dot-atom-text   =   1*atext *("." 1*atext)
                =~  [-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*

dot-atom        =   [CFWS] dot-atom-text [CFWS]
                =~  (([\t ]*\r\n)?[\t ]+)?[-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*(([\t ]*\r\n)?[\t ]+)?
(normalized)    =~  [-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*

qtext           =   %d33 / %d35-91 / %d93-126
                =~  []!#-[^-~]

qcontent        =   qtext / quoted-pair
                =~  []!#-[^-~]|(\\[\t -~])

(erratum)
quoted-string   =   [CFWS] DQUOTE ((1*([FWS] qcontent) [FWS]) / FWS) DQUOTE [CFWS]
                =~  (([\t ]*\r\n)?[\t ]+)?"(((([\t ]*\r\n)?[\t ]+)?([]!#-[^-~]|(\\[\t -~])))+(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?)"(([\t ]*\r\n)?[\t ]+)?
(normalized)    =~  "([]!#-[^-~ \t]|(\\[\t -~]))+"

dtext           =   %d33-90 / %d94-126
                =~  [!-Z^-~]

domain-literal  =   [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]
                =~  (([\t ]*\r\n)?[\t ]+)?\[((([\t ]*\r\n)?[\t ]+)?[!-Z^-~])*(([\t ]*\r\n)?[\t ]+)?](([\t ]*\r\n)?[\t ]+)?
(normalized)    =~  \[[\t -Z^-~]*]

local-part      =   dot-atom / quoted-string
                =~  (([\t ]*\r\n)?[\t ]+)?[-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?"(((([\t ]*\r\n)?[\t ]+)?([]!#-[^-~]|(\\[\t -~])))+(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?)"(([\t ]*\r\n)?[\t ]+)?
(normalized)    =~  [-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+"

domain          =   dot-atom / domain-literal
                =~  (([\t ]*\r\n)?[\t ]+)?[-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?\[((([\t ]*\r\n)?[\t ]+)?[!-Z^-~])*(([\t ]*\r\n)?[\t ]+)?](([\t ]*\r\n)?[\t ]+)?
(normalized)    =~  [-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|\[[\t -Z^-~]*]

addr-spec       =   local-part "@" domain
                =~  ((([\t ]*\r\n)?[\t ]+)?[-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?"(((([\t ]*\r\n)?[\t ]+)?([]!#-[^-~]|(\\[\t -~])))+(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?)"(([\t ]*\r\n)?[\t ]+)?)@((([\t ]*\r\n)?[\t ]+)?[-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*(([\t ]*\r\n)?[\t ]+)?|(([\t ]*\r\n)?[\t ]+)?\[((([\t ]*\r\n)?[\t ]+)?[!-Z^-~])*(([\t ]*\r\n)?[\t ]+)?](([\t ]*\r\n)?[\t ]+)?)
(normalized)    =~  ([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|\[[\t -Z^-~]*])

Zauważ, że niektóre źródła (zwłaszcza w3c ) twierdzą, że RFC 5322 jest zbyt rygorystyczny dla części lokalnej (tj. Części przed znakiem @). Jest tak, ponieważ „..”, „a..b” i „a.” nie są poprawnymi atomami-kropkami, chociaż mogą być używane jako nazwy skrzynek pocztowych. RFC, jednakże nie pozwalają na lokalne części takich jak te, oprócz tego, że muszą one być podane. Zamiast tego a..b@example.netpowinieneś pisać "a..b"@example.net, co jest semantycznie równoważne.

Dalsze ograniczenia

SMTP (zgodnie z definicją w RFC 5321 ) dodatkowo ogranicza zestaw prawidłowych adresów e-mail (a właściwie: nazwy skrzynek pocztowych). Narzucenie tej surowszej gramatyki wydaje się rozsądne, aby dopasowany adres e-mail mógł faktycznie zostać użyty do wysłania wiadomości e-mail.

RFC 5321 w zasadzie pozostawia samą część „lokalną” (tj. Część przed znakiem @), ale jest bardziej rygorystyczna w części domeny (tj. Część po znaku @). Pozwala tylko na nazwy hostów zamiast atomów kropkowych i literałów adresowych zamiast literałów domenowych.

Gramatyka przedstawiona w RFC 5321 jest zbyt łagodna, jeśli chodzi zarówno o nazwy hostów, jak i adresy IP. Pozwoliłem sobie na „poprawienie” omawianych zasad, wykorzystując ten projekt i RFC 1034 jako wytyczne. Oto wynikowe wyrażenie regularne.

([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@([0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?(\.[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?)*|\[((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|IPv6:((((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){6}|::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){5}|[0-9A-Fa-f]{0,4}::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){4}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):)?(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){3}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,2}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){2}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,3}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,4}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,5}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,6}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)|(?!IPv6:)[0-9A-Za-z-]*[0-9A-Za-z]:[!-Z^-~]+)])

Zauważ, że w zależności od przypadku użycia możesz nie chcieć dopuścić „dosłowny adres ogólny” w wyrażeniu regularnym. Zauważ też, że użyłem negatywnego spojrzenia (?!IPv6:)w ostatnim wyrażeniu regularnym, aby zapobiec części „dosłowny adres-dosłowny”, aby dopasować zniekształcone adresy IPv6. Niektóre procesory wyrażeń regularnych nie obsługują negatywnego wyglądu. Usuń podciąg |(?!IPv6:)[0-9A-Za-z-]*[0-9A-Za-z]:[!-Z^-~]+z wyrażenia regularnego, jeśli chcesz usunąć całą część „Ogólny adres-dosłowny”.

Oto pochodna:

Let-dig         =   ALPHA / DIGIT
                =~  [0-9A-Za-z]

Ldh-str         =   *( ALPHA / DIGIT / "-" ) Let-dig
                =~  [0-9A-Za-z-]*[0-9A-Za-z]

(regex is updated to make sure sub-domains are max. 63 charactes long - RFC 1034 section 3.5)
sub-domain      =   Let-dig [Ldh-str]
                =~  [0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?

Domain          =   sub-domain *("." sub-domain)
                =~  [0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?(\.[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?)*

Snum            =   1*3DIGIT
                =~  [0-9]{1,3}

(suggested replacement for "Snum")
ip4-octet       =   DIGIT / %x31-39 DIGIT / "1" 2DIGIT / "2" %x30-34 DIGIT / "25" %x30-35
                =~  25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9]

IPv4-address-literal    =   Snum 3("."  Snum)
                        =~  [0-9]{1,3}(\.[0-9]{1,3}){3}

(suggested replacement for "IPv4-address-literal")
ip4-address     =   ip4-octet 3("." ip4-octet)
                =~  (25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}

(suggested replacement for "IPv6-hex")
ip6-h16         =   "0" / ( (%x49-57 / %x65-70 /%x97-102) 0*3(%x48-57 / %x65-70 /%x97-102) )
                =~  0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}

(not from RFC)
ls32            =   ip6-h16 ":" ip6-h16 / ip4-address
                =~  (0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}

(suggested replacement of "IPv6-addr")
ip6-address     =                                      6(ip6-h16 ":") ls32
                    /                             "::" 5(ip6-h16 ":") ls32
                    / [                 ip6-h16 ] "::" 4(ip6-h16 ":") ls32
                    / [ *1(ip6-h16 ":") ip6-h16 ] "::" 3(ip6-h16 ":") ls32
                    / [ *2(ip6-h16 ":") ip6-h16 ] "::" 2(ip6-h16 ":") ls32
                    / [ *3(ip6-h16 ":") ip6-h16 ] "::"   ip6-h16 ":"  ls32
                    / [ *4(ip6-h16 ":") ip6-h16 ] "::"                ls32
                    / [ *5(ip6-h16 ":") ip6-h16 ] "::"   ip6-h16
                    / [ *6(ip6-h16 ":") ip6-h16 ] "::"
                =~  (((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){6}|::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){5}|[0-9A-Fa-f]{0,4}::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){4}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):)?(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){3}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,2}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){2}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,3}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,4}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,5}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,6}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::

IPv6-address-literal    =   "IPv6:" ip6-address
                        =~  IPv6:((((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){6}|::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){5}|[0-9A-Fa-f]{0,4}::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){4}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):)?(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){3}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,2}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){2}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,3}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,4}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,5}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,6}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)

Standardized-tag        =   Ldh-str
                        =~  [0-9A-Za-z-]*[0-9A-Za-z]

dcontent        =   %d33-90 / %d94-126
                =~  [!-Z^-~]

General-address-literal =   Standardized-tag ":" 1*dcontent
                        =~  [0-9A-Za-z-]*[0-9A-Za-z]:[!-Z^-~]+

address-literal =   "[" ( IPv4-address-literal / IPv6-address-literal / General-address-literal ) "]"
                =~  \[((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|IPv6:((((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){6}|::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){5}|[0-9A-Fa-f]{0,4}::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){4}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):)?(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){3}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,2}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){2}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,3}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,4}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,5}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,6}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)|(?!IPv6:)[0-9A-Za-z-]*[0-9A-Za-z]:[!-Z^-~]+)]

Mailbox         =   Local-part "@" ( Domain / address-literal )
                =~  ([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@([0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?(\.[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?)*|\[((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|IPv6:((((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){6}|::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){5}|[0-9A-Fa-f]{0,4}::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){4}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):)?(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){3}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,2}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){2}|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,3}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,4}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,5}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,6}(0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}))?::)|(?!IPv6:)[0-9A-Za-z-]*[0-9A-Za-z]:[!-Z^-~]+)])

Sprawdzanie poprawności przez użytkownika

Typowym przypadkiem użycia jest sprawdzanie poprawności przez użytkownika, na przykład w formularzu HTML. W takim przypadku zwykle uzasadnione jest wykluczenie literałów adresu i wymaganie co najmniej dwóch etykiet w nazwie hosta. Biorąc za podstawę ulepszoną regex RFC 5321 z poprzedniej sekcji, wynikowe wyrażenie byłoby:

([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?(\.[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?)+

Nie polecam dalszego ograniczania części lokalnej, np. Poprzez wykluczenie ciągów cytowanych, ponieważ nie wiemy, jakie nazwy skrzynek pocztowych są dozwolone przez niektóre hosty (jak, "a..b"@example.neta nawet "a b"@example.net).

Nie polecam też jawnego sprawdzania poprawności listy dosłownych domen najwyższego poziomu ani nawet nakładania ograniczeń długości (pamiętaj, jak unieważniono „.museum” [a-z]{2,4}), ale jeśli musisz:

([-!#-'*+/-9=?A-Z^-~]+(\.[-!#-'*+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@([0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?\.)*(net|org|com|info|itp...)

Upewnij się, że regex jest aktualny, jeśli zdecydujesz się pójść ścieżką jawnej weryfikacji domeny najwyższego poziomu.

Dalsze uwagi

Przy akceptowaniu tylko nazw hostów w części domeny (po znaku @) powyższe wyrażenia regularne akceptują tylko etykiety zawierające maksymalnie 63 znaki, tak jak powinny. Nie wymuszają jednak faktu, że cała nazwa hosta musi mieć co najwyżej 253 znaki (łącznie z kropkami). Chociaż ograniczenie to jest ściśle mówiąc nadal regularne, nie jest możliwe wykonanie wyrażenia regularnego uwzględniającego tę zasadę.

Innym zagadnieniem, szczególnie w przypadku używania wyrażeń regularnych do sprawdzania poprawności danych wejściowych, jest informacja zwrotna dla użytkownika. Jeśli użytkownik wprowadzi niepoprawny adres, dobrze byłoby przekazać nieco więcej informacji zwrotnych niż prosty „adres niepoprawny pod względem składniowym”. W przypadku wyrażeń regularnych „waniliowych” nie jest to możliwe.

Te dwie kwestie można rozwiązać, analizując adres. Dodatkowym ograniczeniem długości nazw hostów można w niektórych przypadkach również zaradzić, używając dodatkowego wyrażenia regularnego, które to sprawdza, i dopasowując adres do obu wyrażeń.

Żadne z wyrażeń regularnych w tej odpowiedzi nie jest zoptymalizowanych pod kątem wydajności. Jeśli wydajność stanowi problem, powinieneś sprawdzić, czy (i jak) wybrany regex można zoptymalizować.


3
RFC 6532 aktualizuje 5322, aby umożliwić i zawierać pełne, czyste UTF-8. Dodatkowe informacje tutaj .

Według wikipedii wydaje się, że lokalna część po kropkowaniu ma ograniczenie do 64 znaków na część, a także RFC 5322 odnosi się do kropkowanej części lokalnej, którą należy interpretować z ograniczeniami domen. Na przykład arbitrary-long-email-address-should-be-invalid-arbitrary-long-email-address-should-be-invalid.and-the-second-group-also-should-not-be-so-long-and-the-second-group-also-should-not-be-so-long@example.comnie powinien sprawdzać poprawności. Sugeruję zmianę znaków „+” w pierwszej grupie (nazwa przed opcjonalną kropką) oraz w drugiej grupie (nazwa po następujących kropkach) na{1,64}
Xavi Montero

Ponieważ komentarze mają ograniczony rozmiar, oto wynikowy wyrażenie regularne, którego zamierzam użyć, czyli ten na początku tej odpowiedzi, a także ograniczenie rozmiaru w części lokalnej, a także dodanie odwrotnego ukośnika przed „/” symbol wymagany przez PHP, a także w regex101.com: W PHP używam:$emailRegex = '/^([-!#-\'*+\/-9=?A-Z^-~]{1,64}(\.[-!#-\'*+\/-9=?A-Z^-~]{1,64})*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?(\.[0-9A-Za-z]([0-9A-Za-z-]{0,61}[0-9A-Za-z])?)+$/';
Xavi Montero

PRZESTROGA: Z jakiegoś powodu StackOverflow dodaje ukryte znaki podczas kopiowania z renderowanego przecena. Skopiuj go do regex101.com, a zobaczysz tam czarne kropki. Musisz je usunąć i skorygować ciąg ... Może jeśli zostaną zintegrowane z odpowiedzią, można je poprawnie skopiować. Przepraszam za niedogodności. Nie chcę dodawać nowej odpowiedzi, ponieważ ta jest właściwa. Nie chcę też bezpośrednio edytować, chyba że społeczność uważa, że ​​należy to włączyć.
Xavi Montero

@XaviMontero Thaks za wkład w Xavi! Czy masz odniesienie do RFC określające limit 64 znaków na lokalnych etykietach części? Jeśli tak, chętnie dostosuję odpowiedź.
Rinke

73

Istnieje wiele przykładów tego w sieci (i myślę, że nawet taki, który w pełni zatwierdza RFC - ale jeśli dziesiątki / setki linii są długie, jeśli pamięć służy). Ludzie zwykle dają się ponieść próbom potwierdzenia tego rodzaju rzeczy. Dlaczego nie sprawdzić, czy ma @ i przynajmniej jeden. i spełnia pewną minimalną długość. Podanie fałszywego adresu e-mail i mimo to dopasowanie go do dowolnego prawidłowego wyrażenia regularnego jest banalne. Domyślam się, że fałszywie pozytywne są lepsze niż fałszywe negatywy.


1
Tak, ale który RFC? :) Ten [RFC ‐ 5322 – validator] ( stackoverflow.com/questions/201323/... ) ma tylko około czterdziestu linii.
tchrist

14
A Nie jest wymagane. TLD może mieć adresy e-mail lub adres IPv6
Sijmen Mulder

1
RFC to nie koniec historii: ICANN nie zezwala już na domeny „bez kropek”: icann.org/news/announcement-2013-08-30-pl
Synchro

64

Decydując, które postacie są dozwolone, pamiętaj o swoich apostrofach i dzielonych znajomych. Nie mam kontroli nad tym, że moja firma generuje mój adres e-mail, używając mojego nazwiska z systemu HR. Obejmuje to apostrof w moim nazwisku. Nie mogę powiedzieć, ile razy blokowano mi interakcję z witryną przez fakt, że mój adres e-mail jest „nieprawidłowy”.


4
Jest to bardzo powszechny problem w programach, które przyjmują nieuzasadnione założenia co do tego, co jest i nie jest dozwolone w imieniu osoby. Nie należy przyjmować takich założeń, wystarczy zaakceptować dowolny znak, który zgodnie z odpowiednimi RFC wymaga.
tchrist

4
Tak. Jestem szczególnie wściekły na programistów odrzucających duże litery w adresach e-mail! Głupie i / lub leniwe.
PhiLho,

63

Ten regex pochodzi z biblioteki Email :: Valid w Perlu . Uważam, że jest najdokładniejszy, pasuje do wszystkich 822. Opiera się na wyrażeniu regularnym z książki O'Reilly:

Wyrażenie regularne zbudowane na przykładzie Jeffreya Friedla w Mastering Regular Expressions ( http://www.ora.com/catalog/regexp/ ).

$RFC822PAT = <<'EOF';
[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\
xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xf
f\n\015()]*)*\)[\040\t]*)*(?:(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\x
ff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015
"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\
xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80
-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*
)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\
\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\
x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x8
0-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n
\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x
80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^
\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040
\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([
^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\
\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\
x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-
\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()
]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\
x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\04
0\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\
n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\
015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?!
[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\
]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\
x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\01
5()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*|(?:[^(\040)<>@,;:".
\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]
)|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^
()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*(?:(?:\([^\\\x80-\xff\n\0
15()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][
^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)|"[^\\\x80-\xff\
n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\
x80-\xff\000-\010\012-\037]*)*<[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?
:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-
\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:@[\040\t]*
(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015
()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()
]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\0
40)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\
[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\
xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*
)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80
-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x
80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t
]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\
\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])
*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x
80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80
-\xff\n\015()]*)*\)[\040\t]*)*)*(?:,[\040\t]*(?:\([^\\\x80-\xff\n\015(
)]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\
\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*@[\040\t
]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\0
15()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015
()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(
\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|
\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80
-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()
]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x
80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^
\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040
\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".
\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff
])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\
\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x
80-\xff\n\015()]*)*\)[\040\t]*)*)*)*:[\040\t]*(?:\([^\\\x80-\xff\n\015
()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\
\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)?(?:[^
(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-
\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\
n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|
\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))
[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff
\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\x
ff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(
?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\
000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\
xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\x
ff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)
*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\x
ff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-
\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)
*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\
]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\]
)[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-
\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\x
ff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(
?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80
-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<
>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x8
0-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:
\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]
*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)
*\)[\040\t]*)*)*>)
EOF

14
O_O, musisz także być mistrzem wyrażeń regularnych, aby zrozumieć, co robi
Chris McGrath

45

Podczas pisania w PHP radzę korzystać z walidacji wbudowanej PHP dla wiadomości e-mail.

filter_var($value, FILTER_VALIDATE_EMAIL)

Jeśli używasz wersji php niższej niż 5.3.6, pamiętaj o tym problemie: https://bugs.php.net/bug.php?id=53091

Jeśli chcesz uzyskać więcej informacji o tym, jak działa sprawdzanie poprawności, zobacz tutaj: Czy PHP filter_var FILTER_VALIDATE_EMAIL faktycznie działa?


dostaje głos, dokładnie to, co chciałem powiedzieć. Nie obsługuje IDN, ale wcześniejsza konwersja na mały kod rozwiązuje ten problem. PHP> = 5.3 ma do tego idn_to_ascii (). Jeden z najlepszych i najłatwiejszych sposobów sprawdzania poprawności wiadomości e-mail.
Taylor


42

Nigdy nie zawracam sobie głowy tworzeniem z moim wyrażeniem regularnym, ponieważ są szanse, że ktoś inny wymyślił już lepszą wersję. Zawsze używam wyrażenia regularnego, aby znaleźć taki, który mi się podoba.


1
Oznaczono to pod względem długości i treści, ale nadal jest to dobry wkład z 41 głosami i nie należy go usuwać.
Czy

37

Nie ma takiego, który byłby naprawdę użyteczny.
Omawiam niektóre problemy w odpowiedzi na pytanie Czy jest biblioteka php do sprawdzania poprawności adresu e-mail? , jest to omawiane również w przypadku rozpoznawania adresu e-mail przez Regexp?

Krótko mówiąc, nie oczekuj, że jeden, użyteczny wyrażenie regularne wykona odpowiednią pracę. A najlepsze wyrażenie regularne potwierdzi składnię, a nie poprawność wiadomości e-mail (adres jhohn@example.com jest poprawny, ale prawdopodobnie zostanie odrzucony ...).


Popraw mnie, jeśli się mylę, ale uważam, że PHP używa wzorców PCRE. Jeśli tak, powinieneś być w stanie stworzyć coś podobnego do wzoru RFC 5322 Abigail .
tchrist

@ tchrist: nie jestem pewien, czy PCRE dogonił tę składnię (którą odkryłem). Jeśli tak, nie jestem pewien, czy PHPRE PCRE dorównało tej wersji PCRE ... Cóż, jeśli dobrze rozumiem tę składnię, możesz również użyć parsera PEG, znacznie bardziej przejrzystego i kompletnego niż wyrażenie regularne.
PhiLho,

PCRE został złapany na tym, ale może PHP nie dogoniła PCRE. ☹
tchrist

36

Jednym prostym wyrażeniem regularnym, które przynajmniej nie odrzucałoby żadnego prawidłowego adresu e-mail, byłoby sprawdzanie czegoś, po którym następuje znak @, a następnie coś, po którym następuje kropka i co najmniej 2 coś. Nie odrzuci niczego, ale po zapoznaniu się ze specyfikacją nie mogę znaleźć żadnego e-maila, który byłby prawidłowy i odrzucony.

email = ~ /.+@[^@]+\.[^@]{2,}$/


3
Właśnie tego szukałem. Niezbyt restrykcyjne, ale upewnia się, że jest tylko 1 @ (ponieważ analizujemy listę i chcemy się upewnić, że nie brakuje żadnych przecinków). Do twojej wiadomości, możesz mieć @ po lewej stronie, jeśli jest w cudzysłowie: Valid_email_addresses , ale to dość grzywka .
Josh

2
Po użyciu zorientowałem się, że nie działa dokładnie. /^[^@]+@[^@]+\.[^@]{2}[^@]*$/ faktycznie sprawdza znak 1 @. Twoje wyrażenie regularne przepuści wiele razy z powodu. * Na końcu.
Josh

1
Dobrze. Nie próbuję odrzucić wszystkich nieprawidłowych, po prostu powstrzymaj się od odrzucania prawidłowego adresu e-mail.
spig

1
Byłoby o wiele lepiej użyć tego: /^[^@]+@[^@]+\.[^@]{2,4}$/upewniając się, że kończy się na 2 do 4 znakach innych niż @. Jak zauważył @Josh, teraz na końcu pozwala na dodatkowe @. Ale możesz to również zmienić na: /^[^@]+@[^@]+\.[^a-z-A-Z]{2,4}$/ponieważ wszystkie domeny najwyższego poziomu są znakami aZ. można wymienić 4z 5lub bardziej pozwalając nazw domen najwyższego poziomu będzie już w przyszłości.
LATA

@FLY, ka @ foo. zwraca poprawne. Czy to powinno według standardów?
SexyBeast,

29

Możesz użyć tego zastosowanego przez wtyczkę jQuery Validation:

/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i

wydaje się, że wykonuje dobrą robotę. Pozwolił: a-b'c_d.e@f-g.hale był w stanie wychwycić nieodpowiednie odmiany, takie jak a-b'c_d.@f-g.hia-b'c_d.e@f-.h
dsdsdsdsd

25

Aby uzyskać najbardziej kompleksową ocenę najlepszego wyrażenia regularnego do sprawdzania poprawności adresu e-mail, zobacz ten link; „ Porównywanie adresu e-mail sprawdzającego poprawność wyrażeń regularnych

Oto aktualne najważniejsze wyrażenie w celach informacyjnych:

/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i

spoon16: Ten link nie jest naprawdę poprawny. Stwierdzenie, że nie może istnieć idealny wzór do sprawdzania poprawności adresów e-mail, jest oczywiście błędne. Państwo może , ale trzeba się upewnić, że postępujesz zgodnie z RFC aż do listu. Musisz także wybrać odpowiedni RFC.
tchrist

„Najlepszy” w tej chwili nie działa z wyrażeniem regularnym java - nawet po prawidłowym ucieczce i konwersji łańcucha.
Eric Chen,

23

Nie wspominając już o tym, że nazwy domen innych niż łacińskie (chińskie, arabskie, greckie, hebrajskie, cyrylica itp.) Będą dozwolone w najbliższej przyszłości . Każdy musi zmienić używane wyrażenie regularne, ponieważ te znaki z pewnością nie będą objęte [a-z]/iani\w . Wszystkie zawiodą.

W końcu najlepszym sposobem na sprawdzenie adresu e-mail jest nadal wysłanie wiadomości e-mail na ten adres w celu potwierdzenia adresu. Jeśli adres e-mail jest częścią uwierzytelnienia użytkownika (rejestracja / login / etc), możesz idealnie połączyć go z systemem aktywacji użytkownika. To znaczy, wyślij e-mail z linkiem z unikalnym kluczem aktywacyjnym na podany adres e-mail i zezwól na logowanie tylko wtedy, gdy użytkownik aktywuje nowo utworzone konto za pomocą linku w e-mailu.

Jeśli celem wyrażenia regularnego jest po prostu szybkie poinformowanie użytkownika w interfejsie użytkownika, że ​​podany adres e-mail nie wygląda we właściwym formacie, najlepiej sprawdzić, czy odpowiada on zasadniczo poniższemu wyrażeniu regularnemu:

^([^.@]+)(\.[^.@]+)*@([^.@]+\.)+([^.@]+)$

Proste. Dlaczego, do cholery, miałbyś przejmować się postaciami używanymi w nazwie i domenie? Obowiązkiem klienta jest podanie prawidłowego adresu e-mail, a nie serwera. Nawet jeśli klient poda poprawny składniowo adres e-mail aa@bb.cc, taki jak , nie gwarantuje to, że jest to prawidłowy adres e-mail. Nikt regex nie może tego pokryć.


4
Zgadzam się, że wysłanie wiadomości uwierzytelniającej jest zwykle najlepszym sposobem na tego rodzaju rzeczy, poprawne składniowo i poprawne to nie to samo. Czuję się sfrustrowany, gdy zmuszony jestem wpisać dwa razy mój adres e-mail w polu „Potwierdzenie”, jak gdybym nie mógł zobaczyć, co wpisałem. W każdym razie kopiuję tylko pierwszy z nich, wydaje się, że jest coraz częściej używany.
PeteT

Zgodzić się! ale to wyrażenie regularne nie wydaje mi się ważne, ponieważ pozwala spacesna @.np. po . test@test.ca com netuważa poprawny adres e-mail za pomocą powyższego wyrażenia regularnego, ponieważ powinien on zwracać nieprawidłowy.
CB4,

20

Specyfikacja HTML5 sugeruje proste wyrażenie regularne do sprawdzania poprawności adresów e-mail:

/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/

Celowo nie jest to zgodne z RFC 5322 .

Uwaga: Wymaganie to jest umyślne naruszenie z RFC 5322 , która definiuje składnię dla adresów e-mail, który jest jednocześnie zbyt surowe (przed @znakiem), zbyt ogólnikowe (po @znaku), a zbyt luźny (co pozwala komentarze, whitespace znaków, i cytowane ciągi znaków w sposób nieznany większości użytkowników), które mają tu praktyczne zastosowanie.

Całkowita długość może być również ograniczona do 254 znaków na RFC 3696 errata 1690 .


Najlepsza odpowiedź! Oto link do rekomendacji w3: w3.org/TR/html5/forms.html#valid-e-mail-address Ten regex jest stosowany przez wiele przeglądarek.
Ryan Taylor

3
To NIE jest najlepsza odpowiedź! Ten wzorzec pasuje do tej całkowicie nieprawidłowy adres: invalid@emailaddress. Nalegam na ostrożność i wiele testów przed użyciem!
Sheridan

@Sheridan, jeśli uważasz, że istnieje problem ze specyfikacją HTML5, możesz go podnieść tutaj: github.com/w3c/html/issues
Luna

Nie dodaje to wiele ponad stackoverflow.com/a/8829363 i IMHO byłby lepszy jako edycja lub komentarz na ten temat.

przykład @ localhost jest prawidłowy, ale w przypadku aplikacji w świecie rzeczywistym możesz wymusić rozszerzenie domeny, wystarczy zmienić ostatnią * na +, aby to osiągnąć (zmiana tej części wzorca z 0+ na 1+ )
Mitch Satchwell

15

Dla żywej demonstracji następujący potwór jest całkiem niezły, ale nadal nie rozpoznaje poprawnie wszystkich składniowo ważnych adresów e-mail: rozpoznaje zagnieżdżone komentarze o głębokości do czterech poziomów.

Jest to zadanie dla analizatora składni, ale nawet jeśli adres jest poprawny pod względem składniowym, nadal może nie być dostarczalny. Czasami musisz zastosować metodę Hillbilly: „Hej, wszyscy, patrzcie na nas!”

// derivative of work with the following copyright and license:
// Copyright (c) 2004 Casey West.  All rights reserved.
// This module is free software; you can redistribute it and/or
// modify it under the same terms as Perl itself.

// see http://search.cpan.org/~cwest/Email-Address-1.80/

private static string gibberish = @"
(?-xism:(?:(?-xism:(?-xism:(?-xism:(?-xism:(?-xism:(?-xism:\
s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^
\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))
|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+
|\s+)*[^\x00-\x1F\x7F()<>\[\]:;@\,.<DQ>\s]+(?-xism:(?-xism:\
s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^
\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))
|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+
|\s+)*)|(?-xism:(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(
?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?
:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x
0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*<DQ>(?-xism:(?-xism:[
^\\<DQ>])|(?-xism:\\(?-xism:[^\x0A\x0D])))+<DQ>(?-xism:(?-xi
sm:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xis
m:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\
]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\
s*)+|\s+)*))+)?(?-xism:(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?
-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:
\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[
^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*<(?-xism:(?-xi
sm:(?-xism:(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^(
)\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(
?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))
|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*(?-xism:[^\x00-\x1F\x7F()<
>\[\]:;@\,.<DQ>\s]+(?:\.[^\x00-\x1F\x7F()<>\[\]:;@\,.<DQ>\s]
+)*)(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))
|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:
(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s
*\)\s*))+)*\s*\)\s*)+|\s+)*)|(?-xism:(?-xism:(?-xism:\s*\((?
:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x
0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xi
sm:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*
<DQ>(?-xism:(?-xism:[^\\<DQ>])|(?-xism:\\(?-xism:[^\x0A\x0D]
)))+<DQ>(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\
]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-x
ism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+
)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*))\@(?-xism:(?-xism:(?-xism:(
?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?
-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^
()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s
*\)\s*)+|\s+)*(?-xism:[^\x00-\x1F\x7F()<>\[\]:;@\,.<DQ>\s]+(
?:\.[^\x00-\x1F\x7F()<>\[\]:;@\,.<DQ>\s]+)*)(?-xism:(?-xism:
\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[
^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+)
)|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)
+|\s+)*)|(?-xism:(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:
(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((
?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\
x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*\[(?:\s*(?-xism:(?-x
ism:[^\[\]\\])|(?-xism:\\(?-xism:[^\x0A\x0D])))+)*\s*\](?-xi
sm:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:
\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(
?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+
)*\s*\)\s*)+|\s+)*)))>(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-
xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\
s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^
\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*))|(?-xism:(?-x
ism:(?-xism:(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^
()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*
(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D])
)|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*(?-xism:[^\x00-\x1F\x7F()
<>\[\]:;@\,.<DQ>\s]+(?:\.[^\x00-\x1F\x7F()<>\[\]:;@\,.<DQ>\s
]+)*)(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+)
)|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism
:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\
s*\)\s*))+)*\s*\)\s*)+|\s+)*)|(?-xism:(?-xism:(?-xism:\s*\((
?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\
x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-x
ism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)
*<DQ>(?-xism:(?-xism:[^\\<DQ>])|(?-xism:\\(?-xism:[^\x0A\x0D
])))+<DQ>(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\
\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-
xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)
+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*))\@(?-xism:(?-xism:(?-xism:
(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(
?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[
^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\
s*\)\s*)+|\s+)*(?-xism:[^\x00-\x1F\x7F()<>\[\]:;@\,.<DQ>\s]+
(?:\.[^\x00-\x1F\x7F()<>\[\]:;@\,.<DQ>\s]+)*)(?-xism:(?-xism
:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:
[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+
))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*
)+|\s+)*)|(?-xism:(?-xism:(?-xism:\s*\((?:\s*(?-xism:(?-xism
:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\(
(?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A
\x0D]))|)+)*\s*\)\s*))+)*\s*\)\s*)+|\s+)*\[(?:\s*(?-xism:(?-
xism:[^\[\]\\])|(?-xism:\\(?-xism:[^\x0A\x0D])))+)*\s*\](?-x
ism:(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism
:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:\s*(?-xism:(?-xism:
(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|)+)*\s*\)\s*))
+)*\s*\)\s*)+|\s+)*))))(?-xism:\s*\((?:\s*(?-xism:(?-xism:(?
>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0D]))|(?-xism:\s*\((?:
\s*(?-xism:(?-xism:(?>[^()\\]+))|(?-xism:\\(?-xism:[^\x0A\x0
D]))|)+)*\s*\)\s*))+)*\s*\)\s*)*)"
  .Replace("<DQ>", "\"")
  .Replace("\t", "")
  .Replace(" ", "")
  .Replace("\r", "")
  .Replace("\n", "");

private static Regex mailbox =
  new Regex(gibberish, RegexOptions.ExplicitCapture); 

12

Zgodnie z oficjalnym standardem RFC 2822 poprawny wyrazy e-mail to

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\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])+)\])

jeśli chcesz go używać w Javie, to naprawdę bardzo proste

import java.util.regex.*;

class regexSample 
{
   public static void main(String args[]) 
   {
      //Input the string for validation
      String email = "xyz@hotmail.com";

      //Set the email pattern string
      Pattern p = Pattern.compile(" (?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"
              +"(?:[\\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])+)\\]");

      //Match the given string with the pattern
      Matcher m = p.matcher(email);

      //check whether match is found 
      boolean matchFound = m.matches();

      if (matchFound)
        System.out.println("Valid Email Id.");
      else
        System.out.println("Invalid Email Id.");
   }
}

1
Twoje wyrażenie regularne nie zawiera pierwszej dużej litery, na przykład Leonardo.davinci@gmail.com, co może być denerwujące dla niektórych użytkowników. Zamiast tego użyj tego:(?:[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\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])+)\])
Kebab Krabby

@KebabKrabby Dzięki, edytuj odpowiedź, zaakceptuję zmianę.
AZ_

Jeśli dodam tę zmianę do odpowiedzi, nie będzie to już RFC 2822, więc nie wiem, czy to prawda.
Kebab Krabby

11

Oto PHP, którego używam. Wybrałem to rozwiązanie w duchu „fałszywe pozytywy są lepsze niż fałszywe negatywy”, jak zadeklarował inny komentator ORAZ odnośnie utrzymania czasu odpowiedzi i obciążenia serwera ... naprawdę nie trzeba marnować zasobów serwera wyrażenie regularne, gdy wyeliminuje to najprostszy błąd użytkownika. W razie potrzeby zawsze możesz to zrobić, wysyłając testową wiadomość e-mail.

function validateEmail($email) {
  return (bool) stripos($email,'@');
}

1
a) „Zasoby serwera odpadów” są nieskończenie małe, ale jeśli masz na to ochotę, możesz to zrobić po stronie klienta za pomocą JS. b) Czego potrzebujesz, aby wysłać wiadomość rejestracyjną, a użytkownik wejdzie do mnie @ sorryotthedotcom? Twoje „rozwiązanie” kończy się niepowodzeniem i tracisz użytkownika.
johnjohn

a) Powoływanie się na sprawdzanie poprawności JS, które zakończy się niepowodzeniem, gdy JavaScript jest wyłączony, również nie wydaje się najlepszym pomysłem (po prostu btw)
auco

11

Standard RFC 5322:

Umożliwia część lokalną dot-atom, część lokalną z cytowanym ciągiem, przestarzałą (mieszaną część kropkową i ciąg cytowany) część lokalną, domenę domeny, (IPv4, IPv6 i adres IPv6 odwzorowany na IPv6) domenę dosłowną, i (zagnieżdżony) CFWS.

'/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD'

Standard RFC 5321:

Pozwala na lokalną część kropkową, część lokalną z cytowanym ciągiem, domenę nazw domen i (dosł. IPv4, IPv6 i adres IPv6 odwzorowany na IPv4) domenę.

'/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!"?(?>\\\[ -~]|[^"]){65,}"?@)(?>([!#-\'*+\/-9=?^-~-]+)(?>\.(?1))*|"(?>[ !#-\[\]-~]|\\\[ -~])*")@(?!.*[^.]{64,})(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?2)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?3)){7}|(?!(?:.*[a-f0-9][:\]]){8,})((?3)(?>:(?3)){0,6})?::(?4)?))|(?>(?>IPv6:(?>(?3)(?>:(?3)){5}:|(?!(?:.*[a-f0-9]:){6,})(?5)?::(?>((?3)(?>:(?3)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?>\.(?6)){3}))\])$/iD'

Podstawowy:

Umożliwia dot-atom lokalną część i nazwę domeny (wymaga co najmniej dwóch etykiet nazw domen z TLD ograniczoną do 2-6 znaków alfabetycznych).

"/^(?!.{255,})(?!.{65,}@)([!#-'*+\/-9=?^-~-]+)(?>\.(?1))*@(?!.*[^.]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?\.){1,126}[a-z]{2,6}$/iD"

Co to za diabelski język? Widzę /Dflagę, a zacytowałeś ją pojedynczymi cudzysłowami, a także użyłeś ukośników do rozgraniczenia wzoru? To nie jest Perl i nie może być PCRE. Czy to zatem PHP? Sądzę, że są to jedyne trzy, które pozwalają na rekurencję (?1).
tchrist

Jest w PHP, który używa PCRE. Ukośniki są używane tylko do ograniczania znaków specjalnych, takich jak nawiasy, nawiasy kwadratowe oraz oczywiście ukośniki i pojedyncze cudzysłowy. Flaga / D, jeśli nie wiesz, ma zapobiegać dodawaniu nowej linii na końcu łańcucha, co w innym przypadku byłoby dozwolone.
MichaelRushton,

9

Dziwne, że „nie możesz” pozwolić na 4 znaki TLD. Jesteś zakazu ludzi z .info i .name , a długość ograniczenie przystanek .travel i .museum , ale tak, są one mniej powszechne niż 2 znaki TLD i 3 znaki TLD.

Powinieneś także dopuścić duże litery. Systemy poczty elektronicznej znormalizują część lokalną i część domeny.

W przypadku wyrażenia regularnego części domeny nazwa domeny nie może zaczynać się od „-” i nie może kończyć się na „-”. Dash może pozostać tylko pomiędzy.

Jeśli korzystałeś z biblioteki PEAR, sprawdź jej funkcję poczty (nie pamiętam dokładnej nazwy / biblioteki). Możesz zweryfikować adres e-mail, wywołując jedną funkcję, a on sprawdza adres e-mail zgodnie z definicją w RFC822.


2
@Joseph Yee: Czy RFC 822 nie jest trochę przestarzały?
tchrist

8
public bool ValidateEmail(string sEmail)
{
    if (sEmail == null)
    {
        return false;
    }

    int nFirstAT = sEmail.IndexOf('@');
    int nLastAT = sEmail.LastIndexOf('@');

    if ((nFirstAT > 0) && (nLastAT == nFirstAT) && (nFirstAT < (sEmail.Length - 1)))
    {
        return (Regex.IsMatch(sEmail, @"^[a-z|0-9|A-Z]*([_][a-z|0-9|A-Z]+)*([.][a-z|0-9|A-Z]+)*([.][a-z|0-9|A-Z]+)*(([_][a-z|0-9|A-Z]+)*)?@[a-z][a-z|0-9|A-Z]*\.([a-z][a-z|0-9|A-Z]*(\.[a-z][a-z|0-9|A-Z]*)?)$"));
    }
    else
    {
        return false;
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.