Как проверить адрес электронной почты с помощью регулярного выражения?

3317

На протяжении многих лет я медленно разрабатывал регулярное выражение, которое корректно проверяет адреса электронной почты MOST, предполагая, что они не используют IP-адрес в качестве серверной части.

Я использую его в нескольких программах PHP, и он работает большую часть времени. Однако время от времени со мной связывается кто-то, у кого проблемы с сайтом, который его использует, и мне приходится вносить некоторые коррективы (совсем недавно я понял, что не разрешаю 4-символьные TLD).

Какое лучшее регулярное выражение вы видели или видели для проверки писем?

Я видел несколько решений, в которых используются функции, использующие несколько более коротких выражений, но я бы предпочел иметь одно длинное сложное выражение в простой функции вместо нескольких коротких выражений в более сложной функции.

acrosman
источник
5
Регулярное выражение, которое может проверить правильность форматирования IDNA, не помещается в stackexchange. (правила канонизации были очень извилистыми и особенно плохо подходящими для обработки регулярных выражений)
Jasen
Регулярные выражения могут быть переменными, так как в некоторых случаях почтовый аргумент может содержать пробел, а в других случаях он не может содержать пробелов.
ểơửṩgǻňạcểơửṩ

Ответы:

2440

Совместимое регулярное выражение полностью RFC 822 является неэффективным и неясным из - за его длиной. К счастью, RFC 822 был заменен дважды, и текущая спецификация адресов электронной почты - RFC 5322 . RFC 5322 приводит к регулярному выражению, которое можно понять, если изучить его в течение нескольких минут, и достаточно эффективно для реального использования.

Одно регулярное выражение, совместимое с RFC 5322, можно найти в верхней части страницы по адресу http://emailregex.com/, но в нем используется шаблон IP-адреса, распространяющийся по Интернету, с ошибкой, которая допускает 00любое из десятичных значений байтов без знака в Адрес, разделенный точками, что недопустимо. Остальная часть, похоже, соответствует грамматике RFC 5322 и проходит несколько тестов с использованием grep -Poдоменных имен, IP-адресов, неверных и учетных записей с кавычками и без них.

Исправляя 00ошибку в шаблоне IP, мы получаем работающее и довольно быстрое регулярное выражение. (Очистите отрендеренную версию, а не уценку, для реального кода.)

(: [А-z0-9 # $% & '* + / = ^ _ `{|} ~ - + (?.!: \ [А-z0-9 # $% &!] * + / ? = ^ _ `{|} ~ -] +) * |" (?: [\ x01- \ x08 \ x0b \ x0c \ x0e- \ x1f \ x21 \ x23- \ X5b \ x5d- \ x7f] | \\ [\ x01- \ x09 \ x0b \ x0c \ x0e- \ x7f]) * ") @ (: (: [а-z0-9] (?:? [а-z0-9 -] * [а-z0 -9]) \) + [а-z0-9] (?.?? [а-z0-9 -] * [а-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]) |? [ а-z0-9 -] * [а-z0-9]: (?: [\ x01- \ x08 \ x0b \ x0c \ x0e- \ x1f \ x21- \ X5a \ x53- \ x7f] | \\ [\ x01- \ x09 \ x0b \ x0c \ x0e- \ x7f]) +) \])

или:

(?:[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])+)\])

Вот диаграмма , из конечного автомата для регулярного выражения выше , которое является более ясным , чем само регулярное выражение введите описание изображения здесь

Более сложные шаблоны в Perl и PCRE (библиотека регулярных выражений, используемая, например, в PHP) могут безошибочно анализировать RFC 5322 . Python и C # тоже могут это делать, но они используют синтаксис, отличный от первых двух. Однако, если вы вынуждены использовать один из многих менее мощных языков сопоставления с образцом, то лучше использовать настоящий парсер.

Также важно понимать, что проверка его в соответствии с RFC абсолютно ничего не говорит вам о том, действительно ли этот адрес существует в предоставленном домене, или является ли лицо, вводящее адрес, его истинным владельцем. Люди постоянно подписывают других на списки рассылки. Исправление, которое требует более причудливого вида проверки, который включает отправку на этот адрес сообщения, содержащего токен подтверждения, который должен быть введен на той же веб-странице, что и адрес.

Жетоны подтверждения - это единственный способ узнать, что вы получили адрес человека, который его вводит. Вот почему большинство списков рассылки теперь используют этот механизм для подтверждения регистрации. В конце концов, любой может отрицать president@whitehouse.gov, и это даже будет восприниматься как законное, но вряд ли это будет человек на другом конце.

Для PHP вы не должны использовать шаблон, приведенный в разделе Проверка адреса электронной почты с PHP, правильный путь, из которого я цитирую:

Существует некоторая опасность того, что обычное использование и широко распространенное неаккуратное кодирование установят фактический стандарт для адресов электронной почты, который является более ограничительным, чем записанный формальный стандарт.

Это не лучше, чем все другие не RFC шаблоны. Он даже не настолько умен, чтобы справиться даже с RFC 822 , не говоря уже о RFC 5322. Этот , однако, таков .

Если вы хотите стать модным и педантичным, внедрите полный двигатель состояния . Регулярное выражение может действовать только как элементарный фильтр. Проблема с регулярными выражениями заключается в том, что говорить кому-то, что их совершенно действительный адрес электронной почты недействителен (ложный положительный результат), потому что ваше регулярное выражение не может его обработать, просто грубо и невежливо с точки зрения пользователя. Механизм состояний для этой цели может проверять и даже корректировать адреса электронной почты, которые в противном случае считались бы недействительными, поскольку он разбирает адрес электронной почты в соответствии с каждым RFC. Это позволяет получить потенциально более приятный опыт, как

Указанный адрес электронной почты «myemail @ address, com» недействителен. Вы имели в виду myemail@address.com?

См. Также Проверка адресов электронной почты , включая комментарии. Или сравнение адреса электронной почты с проверкой правильности регулярных выражений .

Визуализация регулярных выражений

Debuggex Demo

bortzmeyer
источник
180
Вы сказали: «Нет хорошего регулярного выражения». Это общее или особенное для проверки адреса электронной почты?
Томалак
37
@ Томалак: только для адресов электронной почты. Как сказал Борцмейер, RFC чрезвычайно сложен
Luk
37
Журнальная статья о Linux, которую вы упоминаете, на самом деле неверна в нескольких отношениях. В частности, Ловелл явно не прочитал опечатки в RFC3696 и повторяет некоторые ошибки в опубликованной версии RFC. Больше здесь: dominicsayers.com/isemail
Доминик Сэйерс
9
У Джеффа Этвуда есть замечательное регулярное выражение в этом сообщении для проверки всех действительных адресов электронной почты: codinghorror.com/blog/2005/02/regex-use-vs-regex-abuse.html
CMircea,
5
Обратите внимание, что текущая спецификация HTML5 включает в себя регулярное выражение и ABNF для проверки ввода по электронной почте, которая намеренно более ограничительна, чем исходные RFC.
Синхронное
747

Вы не должны использовать регулярные выражения для проверки адресов электронной почты.

Вместо этого используйте класс MailAddress , например:

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

MailAddressКласс использует анализатор BNF для проверки адреса в полном соответствии с RFC822.

Если вы планируете использовать MailAddressдля проверки адреса электронной почты, имейте в виду, что этот подход также принимает часть отображаемого имени адреса электронной почты, и это может быть не совсем тем, чего вы хотите достичь. Например, он принимает эти строки в качестве действительных адресов электронной почты:

  • "user1@hotmail.com; user2@gmail.com"
  • "user1@hotmail.com; user2@gmail.com; user3@company.com"
  • «Отображаемое имя пользователя user3@company.com»
  • "user4 @ company.com"

В некоторых из этих случаев только последняя часть строк обрабатывается как адрес; остальное до этого - отображаемое имя. Чтобы получить простой адрес электронной почты без отображаемого имени, вы можете сравнить нормализованный адрес с исходной строкой.

bool isValid = false;

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

Кроме того, user@company.MailAddress также принимает адрес, имеющий точку в конце, например .

Если вы действительно хотите использовать регулярное выражение, вот оно :

(?: (?: \ r \ n)? [\ t]) * (?: (?: (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031 ] + (?: (?: (?: \ r \ n)? [\ t]
(? = [\ [) + | | \ Z "() <> @,;: \\" \ [\]])) | "(: [^ \?" \ Г \\] | \\.. | (?: (?: \ r \ n)? [\ t])) * "(? :( ?:
\ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \ \ ". \ [\] \ 000- \ 031] + (? :(? :(
?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | "(?: [ ^ \ "\ г \\] | \\ | (:.? (: \ г \ п) [? 
\ t])) * "(?: (?: \ r \ n)? [\ t]) *)) * @ (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 0
31] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\ ]])) | \ [([^ \ [\] \ г \\] |. \\) * \
] (?: (?: \ r \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] +
(?: (?: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]]) ) | \ [([^ \ [\] \ г \\] | \\) * \.] (?:
(?: \ r \ n)? [\ t]) *)) * | (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z
| (? = [\ [ "() <> @,;: \\". \ [\]]).?) | "? (: [^ \" \ Г \\] | \\ | (:( ?: \ r \ n)? [\ t])) * "(?: (?: \ r \ n)
? [\ t]) *) * \ <(?: (?: \ r \ n)? [\ t]) * (?: @ (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \
r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | \ [([^ \ [\ ] \ г \\] | \\) * \]. (?:? (: \ г \ п)? [
 \ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ г \ п)
? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | \ [([^ \ [\] \ r \ \] | \\.) * \] (?: (?: \ r \ n)? [\ t]
) *)) * (?:, @ (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?:? \ г \ п) [
 \ Т]) + | \ Z | (=? [\ [ "() <> @,;: \\". \ [\]])) | \ [([^ \ [\] \ Г \\] | \\.) * \] (?: (?: \ r \ n)? [\ t]) *
) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031 ] + (?: (?: (?: \ r \ n)? [\ t]
) + | \ Z | (= [\ [? "() <> @,;: \\". \ [\]])) | \ [([^ \ [\] \ Г \\] | \\ .) * \] (?: (?: \ r \ n)? [\ t]) *)) *)
*: (?: (?: \ r \ n)? [\ t]) *)? (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) +
| \ Z | (= [\ [ "() <> @,;: \\?". \ [\]]).) | "? (: [^ \" \ Г \\] | \\ | ( ?: (?: \ 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 \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?
: (?: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | \ [([^ \ [\] \ г \\] |. \\) * \]? (:(?
: \ r \ n)? [\ t]) *)) * \> (?: (?: \ r \ n)? [\ t]) *) | (?: [^ () <> @ ,; : \\ ". \ [\] \ 000- \ 031] + (? :(?
: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | "(? : [^ \ "\ г \\] | \\ | (:.? (: \ г \ п)?
[\ t])) * "(?: (?: \ r \ n)? [\ t]) *) *: (?: (?: \ r \ n)? [\ t]) * (?: ? ((: [^ () <> @,;:. \\»\ [\] 
\ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\" . \ [\]])) | "(?: [^ \" \ г \\] |
\\. | (?: (?: \ 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 \ 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)? [\ т]) * [<> @, (^ (.: \\»\ [\?)] 
\ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\" . \ [\]])) | \ [([^ \ [\] \ г \\]
| \\.) * \] (?: (?: \ r \ n)? [\ t]) *)) *) *: (?: (?: \ r \ n)? [\ t]) * )? (?: [^ () <> @,;: \\ ". \ [\] \ 0
00- \ 031] + (?: (?: (?: \ R \ n)? [\ T]) + | \ Z | (? = [\ ["() <> @,;: \\". \ [\]])) | "(?: [^ \" \ г \\] | \\
. | (?: (?: \ 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 | (? = [
\ [ "() <> @,;: \\". \ [\]].?)) | "? (: [^ \" \ Г \\] | \\ | (: (: \ г \ n)? [\ t])) * "(?: (?: \ r \ n)? [\ t
]) *)) * @ (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ R \ n)? [\ T
]) + | \ Z | (=? [\ [ "() <> @,;: \\". \ [\]])) | \ [([^ \ [\] \ Г \\] | \ \.) * \] (?: (?: \ r \ n)? [\ t]) *) (?
: \. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + ( ?: (?: (?: \ r \ n)? [\ t]) + |
\ Z | (= [\ [ "() <> @,;: \\?". \ [\]]).) | \ [([^ \ [\] \ Г \\] | \\) * \] (?: (?: \ 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 | (? = [\ ["
() <> @,;:..? \\»\ [\]])) | \ [([^ \ [\] \ г \\] | \\) * \] (: (: \ г \ n)? [\ t]) *) (?: \. (?: (?: \ r \ n)
? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z | (? = [\ ["() <>

@,;:..? \\»\ [\]])) | \ [([^ \ [\] \ г \\] | \\) * \] (: (: \ г \ п)? [\ t]) *)) * (?:, @ (?: (?: \ r \ n)? [
 \ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ т]) + | \ 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 | (? = [\ [
"() <> @,;: \\" \ [\]])) | "(: [^ \?" \ Г \\] | \\ | (: (: \ г \ п.?). ? [\ t])) * "(?: (?: \ r \ n)? [\ t])
*)) * @ (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t])
+ | \ Z | (= [\ [ "() <> @,;: \\"? \ [\]].)) | \ [([^ \ [\] \ Г \\] | \\. ) * \] (?: (?: \ r \ n)? [\ t]) *) (?: \
. (?: (?: \ r \ n)? [\ t]) * (?: [^ () <> @,;: \\ ". \ [\] \ 000- \ 031] + (?: (?: (?: \ r \ n)? [\ t]) + | \ Z
| (? = [\ [ "() <> @,;: \\". \ [\]]).) | \ [([^ \ [\] \ Г \\] | \\) * \] (?: (?: \ r \ n)? [\ t]) *)) * \> (? :(
?: \ r \ n)? [\ t]) *)) *)?; \ s *)
SLaks
источник
26
Вы обнаружите, что класс MailAddress в .NET 4.0 гораздо лучше проверяет адреса электронной почты, чем в предыдущих версиях. Я сделал несколько значительных улучшений.
Джефф Такер
7
Я думаю, что это вроде ... не работает ... для более простых идентификаторов. a @ b не проверяет. ar@b.com совпадает только до ar @ b, .com не совпадает. Однако что-то вроде «Я есть я» @ [10.10.10.10] действительно работает! :)
Raze
5
Имейте в виду, что эти валидаторы регулярных выражений, соответствующие RFC, пропустят множество адресов электронной почты, которые вы, вероятно, не захотите принимать, например, "a <body / onload = alert (' lol.com?'+document.cookies ) @aa> "который является действительным адресом электронной почты в perl's Email :: Valid (который использует это огромное регулярное выражение) и может использоваться для XSS rt.cpan.org/Public/Bug/Display.html?id=75650
Matthew Lock
9
@MatthewLock: Это не хуже, чем fake@not-a-real-domain.name. Вы не должны полагаться на проверку электронной почты, чтобы предотвратить XSS.
Слакс
10
@ MatthewLock: Нет. Вам нужно избегать SQL-запросов (или, что еще лучше, использовать параметры). Санитарная обработка не является надлежащей защитой.
SLaks
536

Этот вопрос задают много, но я думаю, что вы должны отступить и спросить себя, почему вы хотите проверять адреса электронной почты синтаксически? Какая выгода на самом деле?

  • Это не поймает общие опечатки.
  • Это не мешает людям вводить недействительные или выдуманные адреса электронной почты или вводить чужие адреса.

Если вы хотите проверить правильность электронного письма, у вас нет другого выбора, кроме как отправить электронное письмо с подтверждением и получить ответ от пользователя. Во многих случаях вам все равно придется отправлять письмо-подтверждение по соображениям безопасности или по этическим причинам (например, вы не можете, например, подписать кого-либо на службу против их воли).

JacquesB
источник
92
Возможно, стоит проверить, что они вводили что-то @ что-то в поле при проверке на стороне клиента, просто чтобы поймать простые ошибки - но в целом вы правы.
Мартин Беккет
8
Мартин, я дал тебе +1, только чтобы потом прочитать, что foobar @ dk - это действительное письмо. Это было бы не красиво, но если вы хотите быть совместимыми с RFC и использовать здравый смысл, вы должны обнаружить такие случаи и попросить пользователя подтвердить, что это правильно.
Philfreo
106
@olavk: если кто-то вводит опечатку (например:) me@hotmail, он, очевидно, не получит ваше электронное письмо с подтверждением, и тогда где они? Их больше нет на вашем сайте, и они задаются вопросом, почему они не могут зарегистрироваться. На самом деле нет, они не совсем - они совсем забыли о вас. Однако, если вы можете просто выполнить базовую проверку работоспособности с помощью регулярных выражений, пока они еще с вами, тогда они сразу же поймут эту ошибку, и вы получите счастливого пользователя.
nickf
5
@JacquesB: Вы делаете отличное замечание. Только то, что он проходит проверку в соответствии с RFC, не означает, что это действительно адрес этого пользователя. Иначе все эти president@whitehouse.govадреса указывают на очень занятого главнокомандующего. :)
tchrist
39
Это не должно быть черным или белым. Если электронная почта выглядит неправильно, сообщите об этом пользователю. Если пользователь все еще хочет продолжить, позвольте ему. Не заставляйте пользователя соответствовать вашему регулярному выражению, скорее используйте регулярное выражение в качестве инструмента, чтобы помочь пользователю понять, что может быть ошибка.
ниндзянир
354

Все зависит от того, насколько точно вы хотите быть. Для моих целей, где я просто пытаюсь не пускать такие вещи, как bob @ aol.com(пробелы в электронных письмах) или steve(без домена) или mary@aolcom(без периода до .com), я использую

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

Конечно, это будет соответствовать вещам, которые не являются действительными адресами электронной почты, но это вопрос получения простых простых ошибок.

В это регулярное выражение можно внести любое количество изменений (и некоторые в комментариях к этому ответу), но это просто и легко понять, и это хорошая первая попытка.

Энди Лестер
источник
6
Он не соответствует foobar @ dk, который является действующим и рабочим адресом электронной почты (хотя, вероятно, большинство почтовых серверов не примут его или добавят что-то.)
bortzmeyer
3
Да, это будет. Я предлагаю вам попробовать это самостоятельно. $ perl -le'print q‹foo@bar.co.uk} = ~ /^\S+@\S+\.\S+$/? q {Y}: q {N} '
Энди Лестер
7
@Richard: .входит в \S.
Дэвид Торнли
43
JJJ: Да, это будет много дерьма. Он также будет соответствовать & $ * # $ (@ $ 0 (%)) $ #.) & *) (* $. Для меня, я больше обеспокоен тем, чтобы поймать нечетную опечатку, как mary@aolcomя, полный мусор . YMMV.
Энди Лестер
5
Просто для контроля над @знаками: /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/ jsfiddle.net/b9chris/mXB96
Крис Москини
338

Это зависит от того, что вы подразумеваете под лучшим: если вы говорите о перехвате каждого действующего адреса электронной почты, используйте следующее:

(?:(?:\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 ) Если вы ищете что-то более простое, но которое поймает большинство действительных адресов электронной почты, попробуйте что-то вроде:

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

РЕДАКТИРОВАТЬ: Из ссылки:

Это регулярное выражение будет проверять только адреса, у которых были удалены все комментарии и заменены пробелами (это делает модуль).

Good Person
источник
10
Он не соответствует всем адресам, некоторые должны быть преобразованы в первую очередь. По ссылке: «Это регулярное выражение будет проверять только адреса, у которых были удалены все комментарии и заменены пробелами (это делает модуль)».
час. Оуэнс
47
Можете ли вы привести пример некоторых, email addressкоторые ошибочно проходят через второй, но попадают в более длинное регулярное выражение?
Лазер
4
Несмотря на то, что когда-то мне это нравилось, это валидатор RFC 822, а не RFC 5322 .
tchrist
24
@Lazer in..valid @ example.com будет простым примером. Вам не разрешено иметь две последовательные точки без кавычек в локальной части.
Рэндал Шварц
5
@Mikhail Perl, но вы не должны его использовать.
Хороший человек
287

[ОБНОВЛЕНО] Я собрал все, что я знаю о проверке адреса электронной почты, здесь: http://isemail.info , который теперь не только проверяет, но и диагностирует проблемы с адресами электронной почты. Я согласен со многими комментариями здесь, что валидация является лишь частью ответа; см. мое эссе на http://isemail.info/about .

Насколько я знаю, is_email () остается единственным валидатором, который определенно скажет вам, является ли данная строка действительным адресом электронной почты или нет. Я загрузил новую версию на http://isemail.info/

Я сопоставил контрольные примеры от Кэла Хендерсона, Дейва Чайлда, Фила Хаака, Дуга Ловелла, RFC5322 и RFC 3696. Всего 275 тестовых адресов. Я провел все эти тесты со всеми бесплатными валидаторами, которые смог найти.

Я постараюсь обновлять эту страницу по мере того, как люди улучшат свои валидаторы. Спасибо Кэлу, Майклу, Дейву, Полу и Филу за их помощь и сотрудничество в составлении этих тестов и конструктивную критику моего собственного валидатора .

Люди должны знать об ошибках в RFC 3696 в частности. Три из канонических примеров на самом деле являются недействительными адресами. И максимальная длина адреса составляет 254 или 256 символов, а не 320.

Доминик Сэйерс
источник
Этот валидатор также кажется правильным. [... время проходит ...] Хм, похоже, что это просто RFC 5322, а не 3693 или ошибки в нем.
tchrist
1
Очень хорошо. Здесь мы получаем не только хорошее эссе, но и тестер для проверки, а также библиотеку для загрузки. Хороший ответ!
bgmCoder
Ваш валидатор не поддерживает punycode (RFC 3492). name@öäü.at может быть действительным адресом. (переводится как name@xn--4ca9at.at),
говорит Джозеф, восстановив Монику
Привет, Джозеф. Вы должны попытаться подтвердить, name@xn--4ca9at.atтак как этот код о проверке, а не интерпретации. Если вы хотите добавить переводчик punycode, тогда я с радостью приму запрос на размещение по адресу github.com/dominicsayers/isemail
Доминик Сэйерс,
266

Согласно спецификации 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])?)*$

Контекст:

Действительный адрес электронной почты является строкой , которая соответствует производству ABNF [...].

Примечание: Это требование является умышленным нарушением в RFC 5322 , который определяет синтаксис для адресов электронной почты , которые одновременно слишком строго (до символа «@»), слишком расплывчато (после символа «@»), и слишком слабое ( возможность практического использования комментариев, пробельных символов и строк в кавычках способами, незнакомыми большинству пользователей).

Следующее JavaScript- и Perl-совместимое регулярное выражение является реализацией приведенного выше определения.

/^[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])?)*$/

Rory O'Kane
источник
12
Это интересно. Это нарушение RFC, но преднамеренное и делает sesne. Пример из реальной жизни: gmail игнорирует точки в части до @, поэтому, если ваш адрес электронной почты test@gmail.com, вы можете отправлять электронные письма для проверки. @ Gmail.com или test .... @ gmail.com, оба эти адреса недействительный согласно RFC, но действительный в реальном мире.
Валентина
Я думаю, что последняя часть должна быть '+' вместо '*': ^ [a-zA-Z0-9.! # $% & '* + / =? ^ _ `{|} ~ -] + @ [a- zA-Z0-9 -] + (?: \. [a-zA-Z0-9 -] +) + $
мммммм
7
@mmmmmm john.doe@localhostдействителен. Конечно, в реальном приложении (то есть в сообществе) я бы хотел, чтобы вы предложили заменить * на +
rabudde
3
@valentinas На самом деле, RFC не исключает эти локальные части, но они должны быть указаны. "test...."@gmail.comполностью действителен в соответствии с RFC и семантически эквивалентен test....@gmail.com.
Ринке
Я получаю сообщение об ошибке при попытке отправить электронное письмо с использованием python через ретранслятор моей компании, если я пытаюсь отправить его на адрес с адресом. @ Или .. @. На самом деле это также относится и к _ @. Я скорее удаляю их перед отправкой, чем доверяю получателю.
ndvo
201

Это легко в Perl 5.10 или новее:

/(?(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
Abigail
источник
20
Хотелось бы увидеть это в Python
tdc
4
Я думаю, что только часть addrspecчасти действительно имеет отношение к вопросу. Принятие чего-то большего и переадресация, хотя какая-то другая часть системы, которая не готова принять полные адреса RFC5822, похожа на стрельбу - ваша собственная нога.
Дольмен
3
Отлично (+1), но технически это не регулярное выражение, конечно ... (что было бы невозможно, так как грамматика не является регулярной).
Ринке
10
регулярные выражения перестали быть регулярными некоторое время назад. Это действительно Perl 'регулярное выражение', хотя!
14:00
4
Я настроил тест для этого регулярного выражения на IDEone: ideone.com/2XFecH Однако, это не совсем "отлично". Кто-нибудь захочет вмешаться? Я что-то пропустил?
Майк
159

я использую

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

Который используется в ASP.NET RegularExpressionValidator.

Per Hornshøj-Schierbeck
источник
28
Бу! Мой (опрометчивый) адрес !@mydomain.netотклонен.
Phrogz
3
Согласно этой странице data.iana.org/TLD/tlds-alpha-by-domain.txt нет доменов с одним единственным символом на верхнем уровне, например, «что- то.c», « что- то.а », вот версия, которая поддержка не менее 2 символов: "thing.pl "," Some.us ":^\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w{2,}([-.]\\w+)*$
Томаш Шульц
4
@ Уэйн Уитти. Вы столкнулись с основной проблемой - обслуживать ли подавляющее большинство адресов, или ВСЕ, включая те, которые никто не будет использовать, кроме как для проверки правильности электронной почты.
Патанджали
@TomaszSzulc лишняя обратная косая черта в вашем ответе сбивает с толку, я только исправил это, и поддержка имен доменов 2 символов работает, ^ \ w + ([- +. '] \ W +) * @ \ w + ([-.] \ W +) * \. \ w {2,} ([-.] \ w +) * $
Акиб Мумтаз
2
этот сбой, simon-@hotmail.comкоторый действительно действителен (у нашего клиента был похожий адрес) `
Simon_Weaver
142

Не знаю, что лучше, но этот, по крайней мере, правильный, если на адресах есть комментарии и они заменены пробелами.

Шутки в сторону. Вы должны использовать уже написанную библиотеку для проверки писем. Лучше всего, вероятно, просто отправить электронное письмо с подтверждением на этот адрес.

Кристиан Вест Хансен
источник
2
Насколько я знаю, некоторые библиотеки тоже не правы. Я смутно помню, что в PHP PEAR была такая ошибка.
bortzmeyer
На этой странице также есть заявление об отказе от нескольких вещей из спецификации. что регулярное выражение не поддерживает.
Крис Вест
7
Это спецификация RFC 822, а не спецификация RFC 5322 .
tchrist
12
В конечном счете, он прав в том, что единственный способ по-настоящему подтвердить адрес электронной почты - это отправить ему письмо и дождаться ответа.
Blazemonger
109

Адреса электронной почты, которые я хочу проверить, будут использоваться веб-приложением ASP.NET с использованием пространства имен System.Net.Mail для отправки писем списку людей. Поэтому вместо того, чтобы использовать какое-то очень сложное регулярное выражение, я просто пытаюсь создать экземпляр MailAddress по адресу. Построитель MailAddress сгенерирует исключение, если адрес сформирован неправильно. Таким образом, я знаю, что могу, по крайней мере, получить электронное письмо от двери. Конечно, это проверка на стороне сервера, но как минимум она вам нужна.

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;
    }
}
davcar
источник
3
Хороший вопрос. Даже если эта проверка сервера отклоняет какой-либо действительный адрес, это не является проблемой, так как вы все равно не сможете отправить на этот адрес с использованием этой конкретной серверной технологии. Или вы можете попробовать сделать то же самое, используя стороннюю библиотеку электронной почты, которую вы используете вместо инструментов по умолчанию.
Пользователь
Мне действительно нравится, как это использует .NET Framework - нет смысла изобретать велосипед. Это отлично. Простой, чистый и гарантирует, что вы действительно можете отправить электронное письмо. Отличная работа.
Кори Дом
... да, и для тех, кто интересуется, как он проверяет, взгляните на код в Reflector - его довольно много - и это не регулярное выражение!
Том Картер
2
Просто примечание: класс MailAddress не соответствует RFC5322, если вы просто хотите использовать его для проверки (и не для отправки, а в этом случае это спорный вопрос, как упомянуто выше). См .: stackoverflow.com/questions/6023589/…
porges
Просто небольшая проблема: если вы хотите сделать свой код валидатора на стороне сервера более пригодным для повторного использования (в данном случае или в целом), я предлагаю использовать args.Valueвместо ссылки поле txtEmail.Textжестко закодированным. Последний будет привязывать ваш валидатор к одному экземпляру элемента управления, что может быть в порядке, если у вас есть одно поле электронной почты, но в противном случае это не рекомендуется.
pholpar
109

Быстрый ответ

Используйте следующее регулярное выражение для проверки ввода:

([-!#-'*+/-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])?)+

Адреса, соответствующие этому регулярному выражению:

  • иметь локальную часть (то есть часть перед знаком @), которая строго соответствует RFC 5321/5322,
  • иметь доменную часть (то есть часть после знака @), которая представляет собой имя хоста, по крайней мере, с двумя метками, каждая из которых имеет длину не более 63 символов.

Второе ограничение - это ограничение по RFC 5321/5322.

Подробный ответ

Использование регулярного выражения, которое распознает адреса электронной почты, может быть полезно в различных ситуациях: например, для поиска адресов электронной почты в документе, для проверки ввода пользователя или в качестве ограничения целостности хранилища данных.

Однако следует отметить, что если вы хотите узнать, действительно ли адрес относится к существующему почтовому ящику, ничто не заменит отправку сообщения на этот адрес. Если вы хотите только проверить, является ли адрес грамматически правильным, то вы можете использовать регулярное выражение, но обратите внимание, что ""@[]это грамматически правильный адрес электронной почты, который, безусловно, не ссылается на существующий почтовый ящик.

Синтаксис адресов электронной почты был определен в различных RFC , особенно в RFC 822 и RFC 5322 . RFC 822 следует рассматривать как «оригинальный» стандарт, а RFC 5322 - как последний стандарт. Синтаксис, определенный в RFC 822, является наиболее мягким, и последующие стандарты ограничивают синтаксис все дальше и дальше, когда более новые системы или службы должны распознавать устаревший синтаксис, но никогда не создавать его.

В этом ответе я буду использовать «адрес электронной почты», addr-specкак определено в RFC (то есть jdoe@example.org, но не "John Doe"<jdoe@example.org>, ни some-group:jdoe@example.org,mrx@exampel.org;).

Существует одна проблема с переводом синтаксисов RFC в регулярные выражения: синтаксис не является регулярным! Это объясняется тем, что они допускают необязательные комментарии в адресах электронной почты, которые могут быть бесконечно вложенными, в то время как бесконечная вложенность не может быть описана регулярным выражением. Для сканирования или проверки адресов, содержащих комментарии, вам нужен анализатор или более мощные выражения. (Обратите внимание, что такие языки, как Perl, имеют конструкции для описания контекстно-свободных грамматик в форме регулярных выражений.) В этом ответе я проигнорирую комментарии и рассмотрю только правильные регулярные выражения.

RFC определяют синтаксис для сообщений электронной почты, а не для адресов электронной почты как таковых. Адреса могут появляться в различных полях заголовка, и именно здесь они в основном определены. Когда они появляются в полях заголовка, адреса могут содержать (между лексическими токенами) пробелы, комментарии и даже разрывы строк. Семантически это не имеет значения, однако. Удаляя этот пробел и т. Д. Из адреса, вы получаете семантически эквивалентное каноническое представление . Таким образом, каноническое представление first. last (comment) @ [3.5.7.9]есть first.last@[3.5.7.9].

Различные синтаксисы должны использоваться для разных целей. Если вы хотите сканировать адреса электронной почты в (возможно, очень старом) документе, возможно, будет хорошей идеей использовать синтаксис, определенный в RFC 822. С другой стороны, если вы хотите проверить ввод пользователя, вы можете использовать синтаксис, определенный в RFC 5322, возможно, принимает только канонические представления. Вы должны решить, какой синтаксис применяется в вашем конкретном случае.

В этом ответе я использую «расширенные» регулярные выражения POSIX, предполагая набор символов, совместимый с ASCII.

RFC 822

Я пришел к следующему регулярному выражению. Я приглашаю всех попробовать и сломать это. Если вы найдете какие-либо ложные срабатывания или ложные отрицания, пожалуйста, оставьте их в комментарии, и я постараюсь исправить выражение как можно скорее.

([^][()<>@,;:\\". \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 822, включая ошибки . Он распознает только адреса электронной почты в их канонической форме. Для регулярного выражения, которое распознает (сворачивание) пробелы, смотрите вывод ниже.

Вывод показывает, как я пришел к выражению. Я перечисляю все соответствующие грамматические правила из RFC в том виде, в котором они отображаются, а затем соответствующее регулярное выражение. Там, где опечатка была опубликована, я даю отдельное выражение для исправленного правила грамматики (помеченное «erratum») и использую обновленную версию как подвыражение в последующих регулярных выражениях.

Как указано в пункте 3.1.4. RFC 822 необязательный линейный пробел может быть вставлен между лексическими токенами. Там, где это применимо, я расширил выражения для соответствия этому правилу и пометил результат как «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

Я пришел к следующему регулярному выражению. Я приглашаю всех попробовать и сломать это. Если вы найдете какие-либо ложные срабатывания или ложные отрицания, пожалуйста, оставьте их в комментарии, и я постараюсь исправить выражение как можно скорее.

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

Я считаю, что он полностью соответствует RFC 5322, включая ошибки . Он распознает только адреса электронной почты в их канонической форме. Для регулярного выражения, которое распознает (сворачивание) пробелы, смотрите вывод ниже.

Вывод показывает, как я пришел к выражению. Я перечисляю все соответствующие грамматические правила из RFC в том виде, в котором они отображаются, а затем соответствующее регулярное выражение. Для правил, которые включают семантически нерелевантные (складывающиеся) пробелы, я даю отдельное регулярное выражение с пометкой «(нормализовано)», которое не принимает этот пробел.

Я проигнорировал все "обс-" правила из RFC. Это означает, что регулярные выражения соответствуют только адресам электронной почты, которые строго соответствуют RFC 5322. Если вам нужно сопоставить «старые» адреса (как делает более свободная грамматика, включая правила «obs-»), вы можете использовать одно из регулярных выражений RFC 822 из предыдущего абзаца.

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^-~]*])

Обратите внимание, что некоторые источники (в частности, w3c ) утверждают, что RFC 5322 является слишком строгим в локальной части (то есть в части, предшествующей знаку @). Это потому что "..", "a..b" и "a." не являются действительными точечными атомами, в то время как они могут использоваться как имена почтовых ящиков. RFC, однако, это позволит местным частям , как это, за исключением того, что они должны быть заключены в кавычки. Поэтому вместо a..b@example.netвас следует написать "a..b"@example.net, что семантически эквивалентно.

Дальнейшие ограничения

SMTP (как определено в RFC 5321 ) дополнительно ограничивает набор действительных адресов электронной почты (или фактически: имена почтовых ящиков). Представляется разумным навязать эту более строгую грамматику, чтобы сопоставленный адрес электронной почты мог фактически использоваться для отправки электронного письма.

RFC 5321, в основном, оставляет в стороне "локальную" часть (то есть часть до @ -знака), но является более строгой в доменной части (т.е. часть после @ -знака). Он допускает только имена хостов вместо точечных атомов и адресные литералы вместо доменных литералов.

Грамматика, представленная в RFC 5321, слишком мягкая, когда речь идет об именах хостов и IP-адресах. Я позволил себе «исправить» эти правила, используя этот проект и RFC 1034 в качестве руководства. Вот результирующее регулярное выражение.

([-!#-'*+/-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^-~]+)])

Обратите внимание, что в зависимости от варианта использования вы можете не использовать «General-address-literal» в своем регулярном выражении. Также обратите внимание, что (?!IPv6:)в последнем регулярном выражении я использовал отрицательный прогноз, чтобы часть «General-address-literal» не соответствовала искаженным адресам IPv6. Некоторые процессоры регулярных выражений не поддерживают негативную перспективу. Удалите подстроку |(?!IPv6:)[0-9A-Za-z-]*[0-9A-Za-z]:[!-Z^-~]+из регулярного выражения, если вы хотите убрать всю часть "General-address-literal".

Вот вывод:

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^-~]+)])

Проверка ввода пользователя

Распространенным вариантом использования является проверка ввода пользователя, например, в форме HTML. В этом случае обычно разумно исключать адресные литералы и требовать по крайней мере две метки в имени хоста. Взяв за основу улучшенное регулярное выражение RFC 5321 из предыдущего раздела, получим следующее выражение:

([-!#-'*+/-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])?)+

Я не рекомендую ограничивать локальную часть, например, путем исключения строк в кавычках, поскольку мы не знаем, какие имена почтовых ящиков допускают некоторые хосты (например, "a..b"@example.netили даже "a b"@example.net).

Я также не рекомендую явно проверять список литеральных доменов верхнего уровня или даже устанавливать ограничения длины (помните, как «.museum» аннулирован [a-z]{2,4}), но если вы должны:

([-!#-'*+/-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|так далее...)

Убедитесь, что вы регулярно обновляете свое регулярное выражение, если решите пойти по пути явной проверки домена верхнего уровня.

Дальнейшие соображения

Когда принимаются только имена хостов в доменной части (после знака @), приведенные выше регулярные выражения принимают только метки, содержащие не более 63 символов, как они должны. Однако они не приводят в исполнение тот факт, что полное имя хоста должно быть длиной не более 253 символов (включая точки). Хотя это ограничение, строго говоря, все еще остается регулярным, сделать регулярное выражение, включающее это правило, не представляется возможным.

Еще одним соображением, особенно при использовании регулярных выражений для проверки ввода, является обратная связь с пользователем. Если пользователь вводит неправильный адрес, было бы неплохо дать немного больше отзывов, чем простой «синтаксически неправильный адрес». С "ванильными" регулярными выражениями это невозможно.

Эти два соображения могут быть решены путем анализа адреса. В некоторых случаях ограничение дополнительной длины для имен хостов также может быть устранено с помощью дополнительного регулярного выражения, которое проверяет его, и сопоставляя адрес с обоими выражениями.

Ни одно из регулярных выражений в этом ответе не оптимизировано для производительности. Если производительность является проблемой, вы должны посмотреть, можно ли (и каким образом) регулярное выражение по вашему выбору.

Rinke
источник
3
RFC 6532 обновляет 5322, чтобы включить и включить полный, чистый UTF-8. Дополнительные подробности здесь .
Согласно википедии, кажется, что локальная часть, если она пунктирная, имеет ограничение в 64 символа на часть, а также RFC 5322 относится к пунктирной локальной части, которая должна интерпретироваться с ограничениями доменов. Например, 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.comне следует проверять. Я предлагаю изменить знаки «+» в первой группе (имя перед необязательной точкой) и во второй группе (имя после следующих точек) на{1,64}
Xavi Montero
Поскольку комментарии ограничены по размеру, вот результирующее регулярное выражение, которое я планирую использовать, - это то, которое находится в начале этого ответа, плюс ограничение размера в локальной части, плюс добавление обратной косой черты перед "/" символ в соответствии с требованиями PHP, а также в regex101.com: В PHP я использую:$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
ПРЕДУПРЕЖДЕНИЕ. По какой-то причине StackOverflow добавляет скрытые символы при копировании из отображаемой уценки. Скопируйте его в regex101.com, и вы увидите там черные точки. Вы должны удалить их и исправить строку ... Может быть, если они включены в ответ, то они правильно копируются. Приносим извинения за неудобства. Я не хочу добавлять новый ответ, так как этот является правильным. Также я не хочу редактировать напрямую, если сообщество не считает, что это должно быть интегрировано в него.
Хави Монтеро
@XaviMontero Такс за вклад Хави! У вас есть ссылка на RFC с указанием ограничения в 64 символа на метках локальной детали? Если это так, я бы с удовольствием скорректировал ответ.
Ринке
73

Есть множество примеров этого в сети (и я думаю, что даже тот, который полностью проверяет RFC - но это десятки / сотни строк, если память служит). Люди склонны увлекаться проверкой подобных вещей. Почему бы просто не проверить, есть ли у него @ и хотя бы один. и встречает некоторую простую минимальную длину. Вводить фальшивое электронное письмо и в любом случае сопоставлять любое действительное регулярное выражение просто. Я предполагаю, что ложные срабатывания лучше, чем ложные.

Draemon
источник
1
Да, но какой RFC? :) Этот [RFC-5322-валидатор] ( stackoverflow.com/questions/201323/… ) имеет длину всего около сорока строк.
tchrist
14
А. не требуется. У TLD могут быть адреса электронной почты или адрес IPv6
Sijmen Mulder
1
RFC - это не конец истории: ICANN больше не допускает домены без точек
Synchro
64

Решая, какие символы разрешены, пожалуйста, помните своих друзей-апострофов и переносов. Я не могу контролировать тот факт, что моя компания генерирует мой адрес электронной почты, используя мое имя из системы управления персоналом. Это включает в себя апостроф в моей фамилии. Я не могу сказать вам, сколько раз я был заблокирован от взаимодействия с веб-сайтом из-за того, что мой адрес электронной почты "недействителен".

ДОК
источник
4
Это очень распространенная проблема в программах, которые делают необоснованные предположения о том, что есть и что не разрешено на имя человека. Никто не должен делать такие предположения, просто принять любой характер, который, по мнению соответствующих RFC, необходимо.
tchrist
4
Да. Я особенно взбешен тем, что программисты отвергают заглавные буквы в адресах электронной почты! Глупо и / или лениво.
Фил
63

Это регулярное выражение из библиотеки Perl Email :: Valid . Я считаю, что он самый точный, он соответствует всем 822. И он основан на регулярном выражении в книге О'Рейли:

Регулярное выражение построено с использованием примера Джеффри Фридла в статье « Освоение регулярных выражений» ( 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
О_О, вам также нужно быть мастером регулярных выражений, чтобы понять, что он делает
Крис МакГрат,
45

Когда вы пишете на PHP, я бы посоветовал вам использовать встроенную проверку PHP для электронной почты.

filter_var($value, FILTER_VALIDATE_EMAIL)

Если вы используете php-версию ниже 5.3.6, пожалуйста, учтите эту проблему: https://bugs.php.net/bug.php?id=53091

Если вам нужна дополнительная информация о том, как работает эта встроенная проверка, см. Здесь: Работает ли PHP filter_var FILTER_VALIDATE_EMAIL на самом деле?

оборота SimonSimCity
источник
получает голос, именно то, что я собирался сказать. Не обрабатывает IDN, но преобразование в маленький код заранее решает это. В PHP> = 5.3 для этого есть idn_to_ascii (). Один из лучших и простых способов проверки электронной почты.
Тейлор
43

Кэл Хендерсон (Flickr) написал статью под названием « Парсинг адресов электронной почты в PHP» и показывает, как правильно выполнять синтаксический анализ адресов электронной почты , соответствующих RFC (2) 822. Вы также можете получить исходный код в php , python и ruby, который лицензирован cc .

adnam
источник
он сказал мне, что a@bэто действительно
dsdsdsdsd
1
@dsdsdsdsd Потому a@bчто допустимо ... в данном случае bэто домен верхнего уровня.
rink.attendant.6
42

Я никогда не удосужился создавать свои собственные регулярные выражения, потому что есть вероятность, что кто-то другой уже придумал лучшую версию. Я всегда использую regexlib, чтобы найти тот, который мне по вкусу.

Kon
источник
1
Это было отмечено для длины и содержания, но это все еще хороший вклад с 41 голосом и не должен быть удален.
Будет ли
37

Нет такого, который действительно пригоден для использования.
Я обсуждаю некоторые вопросы в своем ответе на вопрос: есть ли библиотека php для проверки адреса электронной почты? , это также обсуждается в Regexp распознавание адреса электронной почты трудно?

Короче говоря, не ожидайте, что одно, пригодное для использования регулярное выражение выполнит правильную работу. И наилучшее регулярное выражение будет проверять синтаксис, а не действительность электронного письма (jhohn@example.com верно, но оно, вероятно, отскочит ...).

PhiLho
источник
Поправьте меня, если я ошибаюсь, но я считаю, что PHP использует шаблоны PCRE. Если это так, вы сможете создать нечто похожее на паттерн Abigail RFC 5322 .
tchrist
@tchrist: не уверен, догнал ли PCRE этот синтаксис (который я обнаружил). Если это так, не уверен, что PHP PCRE догнал эту версию PCRE ... Что ж, если я правильно понимаю этот синтаксис, вы также можете использовать PEG-парсер, намного более понятный и полный, чем регулярное выражение в любом случае.
Фил
PCRE уже догнал его, но , возможно , PHP не догнали PCRE. ☹
четверг
36

Одно простое регулярное выражение, которое, по крайней мере, не будет отклонять какой-либо действительный адрес электронной почты, будет проверять что-то, за которым следует знак @, а затем что-то, за которым следует точка и как минимум 2 символа. Он не будет ничего отклонять, но после просмотра спецификации я не смог найти ни одного письма, которое было бы действительным и отклонено.

электронная почта = ~ /.+@[^@]+\.[^@]{2,}$/

spig
источник
3
Это то, что я искал. Не очень ограничительно, но гарантирует, что есть только 1 @ (так как мы разбираем список и хотим убедиться, что нет пропущенных запятых). К вашему сведению, вы можете иметь @ слева, если оно в кавычках: Valid_email_addresses , но это довольно бахромой.
Джош
2
После использования понял, что это не работает точно. /^[^@]+@[^@]+\.[^@]{2}[^@]*$/ на самом деле проверяет на 1 знак @. Ваше регулярное выражение пропустит множественное число из-за. * В конце.
Джош
1
Правильно. Я не пытаюсь отклонить все недействительные, просто не допустите отклонения действительного адреса электронной почты.
spig
1
Было бы намного лучше использовать это: /^[^@]+@[^@]+\.[^@]{2,4}$/удостовериться, что оно заканчивается от 2 до 4 не @ символов. Как указал @Josh, теперь в конце можно добавить лишний @. Но вы также можете изменить это на: /^[^@]+@[^@]+\.[^a-z-A-Z]{2,4}$/все домены верхнего уровня являются символами aZ. вы можете заменить 4с 5или более позволяя доменов верхнего уровня имена , чтобы быть больше в будущем.
FLY
@FLY, ка @ фу . возвращается правильно. Это по стандартам?
SexyBeast
29

Вы можете использовать тот, который используется плагином 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
хаос
источник
это, кажется, делает хорошую работу. Это позволило: a-b'c_d.e@f-g.hно смог поймать несоответствующие изменения, такие как a-b'c_d.@f-g.hиa-b'c_d.e@f-.h
dsdsdsdsd
25

Для наиболее полной оценки лучшего регулярного выражения для проверки адреса электронной почты, пожалуйста, перейдите по этой ссылке; « Сравнение адреса электронной почты, проверяющего регулярные выражения »

Вот текущее верхнее выражение для справочных целей:

/^([\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: Эта ссылка не совсем правильная. Заявление о том, что не может быть идеального шаблона для проверки адресов электронной почты, явно ошибочно. Вы можете , но вы должны убедиться, что вы следуете RFC вплоть до буквы. И вы должны выбрать правильный RFC тоже.
tchrist
«Лучший» в настоящее время не работает с регулярным выражением Java - даже после правильного экранирования и преобразования строки.
Эрик Чен
23

Не говоря уже о том, что нелатинские (китайский, арабский, греческий, иврит, кириллица и т. Д.) Доменные имена должны быть разрешены в ближайшем будущем . Каждый должен изменить используемое регулярное выражение электронной почты, потому что эти символы наверняка не будут охвачены [a-z]/iни тем, ни другим \w. Они все потерпят неудачу.

В конце концов, лучший способ проверить адрес электронной почты по-прежнему состоит в том, чтобы фактически отправить электронное письмо на указанный адрес для подтверждения адреса. Если адрес электронной почты является частью аутентификации пользователя (регистрация / логин / и т. Д.), То вы можете идеально сочетать его с системой активации пользователя. Т.е. отправьте электронное письмо со ссылкой с уникальным ключом активации на указанный адрес электронной почты и разрешите вход в систему только тогда, когда пользователь активировал вновь созданную учетную запись, используя ссылку в электронном письме.

Если целью регулярного выражения является просто быстрое информирование пользователя в пользовательском интерфейсе о том, что указанный адрес электронной почты не выглядит в правильном формате, лучше всего проверить, соответствует ли он в основном следующему регулярному выражению:

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

Просто как тот. С какой стати вы заботитесь о символах, используемых в имени и домене? Ответственность за ввод действительного адреса электронной почты лежит на клиенте, а не на сервере. Даже когда клиент вводит синтаксически действительный адрес электронной почты, например aa@bb.cc, это не гарантирует, что это допустимый адрес электронной почты. Ни одно регулярное выражение не может покрыть это.

BalusC
источник
4
Я согласен, что отправка сообщения аутентификации, как правило, является лучшим способом для такого рода вещей, синтаксически правильные и действительные не совпадают. Я расстраиваюсь, когда меня заставляют дважды вводить свой адрес электронной почты для «Подтверждения», как будто я не могу посмотреть, что я напечатал. В любом случае, я копирую только первое во второе, оно, похоже, все больше используется.
Пит
согласна! но это регулярное выражение, я не думаю, является действительным, потому что это позволяет spacesпосле, @.например. test@test.ca com netсчитается правильным адресом электронной почты, используя приведенное выше регулярное выражение, где, как это должно быть, возвращается недействительным
CB4
20

Спецификация 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])?)*$/

Это намеренно не соответствует RFC 5322 .

Примечание: Это требование является умышленным нарушением в RFC 5322 , который определяет синтаксис для адресов электронной почты, которая одновременно слишком строго (до @символа), слишком расплывчаты (после @символа), и слишком слабым ( с учетом комментариев, пробельные символы, и приведенные строки в манерах, незнакомых большинству пользователей) для практического использования здесь.

Общая длина также может быть ограничена 254 символами, согласно RFC 3696 опечаток 1690 .

Росс Аллан
источник
Лучший ответ! Вот ссылка на рекомендацию w3: w3.org/TR/html5/forms.html#valid-e-mail-address. Это регулярное выражение используется многими браузерами.
Райан Тейлор
3
Это ТАК не лучший ответ! Эта модель соответствует этому совершенно недопустимому адресу: invalid@emailaddress. Я призываю к осторожности и много испытаний, прежде чем использовать его!
Шеридан
@Sheridan, если вы думаете, что есть проблема со спецификацией HTML5, вы можете поднять эту проблему здесь: github.com/w3c/html/issues
Luna
Это не добавляет много по сравнению с stackoverflow.com/a/8829363 и будет ИМХО лучше в качестве редактирования или комментария по этому поводу.
пример @ localhost действителен, но для реального приложения вы можете использовать расширение домена, все, что вам нужно сделать, это изменить конечный * на +, чтобы добиться этого (изменив эту часть шаблона с 0+ на 1+ )
Митч Сэтчвелл
15

Для яркой демонстрации следующий монстр довольно хорош, но все еще не может правильно распознать все синтаксически допустимые адреса электронной почты: он распознает вложенные комментарии глубиной до четырех уровней.

Это задание для синтаксического анализатора, но даже если адрес синтаксически допустим, он все равно может не быть доставленным. Иногда вам приходится прибегать к простому методу: «Эй, вы все, смотрите нас!»

// 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

Согласно официальному стандарту RFC 2822 действительное регулярное выражение электронной почты

(?:[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])+)\])

если вы хотите использовать его в Java, это действительно очень легко

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.");
   }
}
AZ_
источник
1
Ваше регулярное выражение не содержит первую заглавную букву, например, Leonardo.davinci@gmail.com, что может раздражать некоторых пользователей. Используйте это вместо этого:(?:[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 Спасибо, пожалуйста, отредактируйте ответ, я приму изменение.
AZ_
Если я добавлю это изменение в ваш ответ, это больше не будет RFC 2822, поэтому я не знаю, правильно ли это.
Кебаб Крабби
11

Вот PHP, который я использую. Я выбрал это решение в духе «ложные срабатывания лучше, чем ложные отрицания», как было объявлено другим комментатором здесь И в отношении сохранения времени отклика и нагрузки на сервер ... на самом деле нет необходимости тратить ресурсы сервера на регулярное выражение, когда это отсеет самую простую ошибку пользователя. Вы всегда можете выполнить это, отправив тестовое электронное письмо, если хотите.

function validateEmail($email) {
  return (bool) stripos($email,'@');
}
макинтош
источник
1
а) «Ресурсы ненужного сервера» бесконечно малы, но если вы так склонны, вы можете сделать это на стороне клиента с помощью JS. б) Что вам нужно, чтобы отправить регистрационное письмо, и пользователь вводит меня @ Forgotthedotcom? Ваше «решение» терпит неудачу, и вы теряете пользователя.
Джоннон
а) Полагаться на проверку JS, которая не
удалась
11

Стандарт RFC 5322:

Позволяет использовать локальную часть точечного атома, локальную часть в кавычках, устаревшую (смешанная точка и строка в кавычках) локальную часть, домен доменных имен, литеральный домен домена (IPv4, IPv6 и сопоставленный с IPv4 адрес IPv6), и (вложенные) 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'

Стандарт RFC 5321:

Разрешает локальную часть локальной точки, локальную часть в кавычках, домен доменного имени и буквенный домен (IPv4, IPv6 и IPv4-сопоставленный адрес IPv6).

'/^(?!(?>"?(?>\\\[ -~]|[^"])"?){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'

Основные:

Разрешает использование локальной точки и домена доменных имен с точечным атомом (требуются как минимум две метки доменного имени с ДВУ, ограниченным 2-6 буквенными символами).

"/^(?!.{255,})(?!.{65,}@)([!#-'*+\/-9=?^-~-]+)(?>\.(?1))*@(?!.*[^.]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?\.){1,126}[a-z]{2,6}$/iD"
MichaelRushton
источник
Что это за дьявольский язык? Я вижу /Dфлаг, а вы его цитируете одинарными кавычками, но также использовали косую черту для разделения шаблона? Это не Perl, и это не может быть PCRE. Поэтому это PHP? Я считаю, что это единственные три, которые допускают рекурсию (?1).
tchrist
Это на PHP, который использует PCRE. Косая черта используется только для разделения специальных символов, таких как скобки, квадратные скобки и, конечно, косая черта и одинарные кавычки. Флаг / D, если вы не знали, предназначен для предотвращения добавления новой строки в конец строки, что было бы разрешено в противном случае.
MichaelRushton
9

Странно, что вы «не можете» разрешить 4-х символьные TLD. Вы запрещаете людям доступ к .info и .name , и ограничение длины останавливается .travel и .museum , но да, они встречаются реже, чем 2-символьные TLD и 3-символьные TLD.

Вы должны разрешить прописные алфавиты тоже. Системы электронной почты нормализуют локальную часть и часть домена.

Для вашего регулярного выражения доменной части доменное имя не может начинаться с «-» и не может заканчиваться «-». Тире может оставаться только между

Если вы использовали библиотеку PEAR, проверьте их почтовую функцию (забыл точное имя / библиотеку). Вы можете проверить адрес электронной почты, вызвав одну функцию, и он подтвердит адрес электронной почты в соответствии с определением в RFC822.

Джозеф Йи
источник
2
@ Джозеф Йи: Разве RFC 822 немного не устарел?
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;
    }
}
Murthy Jeedigunta
источник