Regexp распознавание адреса электронной почты трудно? - PullRequest
56 голосов
/ 01 октября 2008

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

Кто-нибудь может дать некоторое представление о том, почему это так?

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

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

Ответы [ 19 ]

58 голосов
/ 01 октября 2008

Для формальной спецификации электронной почты, да, это технически невозможно через Regex из-за рекурсии таких вещей, как комментарии (особенно если вы сначала не удаляете комментарии в пробел), а также различные разные форматы (адрес электронной почты не всегда somebody@somewhere.tld). Вы можете приблизиться (с некоторыми массивными и непонятными шаблонами Regex), но гораздо лучший способ проверить электронную почту - это сделать очень знакомое рукопожатие:

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

    1. адрес электронной почты правильный
    2. существует
    3. им принадлежит

Намного лучше, чем слепо принимать адрес электронной почты.

20 голосов
/ 01 октября 2008

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

Mail::VRFY будет выполнять синтаксические и сетевые проверки (и SMTP-сервер где-нибудь принимает этот адрес)

https://metacpan.org/pod/Mail::VRFY

RFC::RFC822::Address - анализатор адресов электронной почты с рекурсивным спуском.

https://metacpan.org/pod/RFC::RFC822::Address

Mail::RFC822::Address - проверка адреса на основе регулярных выражений, на которую стоит обратить внимание только на безумное регулярное выражение

http://ex -parrot.com / ~ PDW / Mail-RFC822-Address.html

Подобные инструменты существуют для других языков. Безумное регулярное выражение ниже ...

(?:(?:\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*)
10 голосов
/ 01 октября 2008

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

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

Если вы просто хотите быть уверены, что пользователь вводит что-то, похожее на электронное письмо, а не просто как «asdf», тогда проверьте @. Более сложная проверка на самом деле не дает никакой выгоды.

(я знаю, что это не отвечает на ваши вопросы, но я думаю, что стоит упомянуть в любом случае)

8 голосов
/ 10 февраля 2009

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

Я провел все эти тесты со всеми валидаторами, которые смог найти. Сравнение здесь: http://www.dominicsayers.com/isemail

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

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

7 голосов
/ 01 октября 2008

В BNF существует контекстно-свободная грамматика, которая описывает действительные адреса электронной почты в RFC-2822 Это сложно. Например:

" @ "@example.com

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

7 голосов
/ 01 октября 2008

Это не все чепуха, хотя разрешающие символы, такие как «+», могут быть очень полезны для пользователей, борющихся со спамом, например, myemail+sketchysite@gmail.com ( мгновенные одноразовые адреса Gmail ).

Только тогда, когда сайт принимает это.

6 голосов
/ 03 октября 2008

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

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

Тем не менее, для остальных из нас, в основном, мы заинтересованы в том, чтобы что-то, что пользователь вводит в веб-форму, выглядело разумным и не имело какой-либо SQL-инъекции или переполнения буфера.

Честно говоря, кто-нибудь действительно хочет, чтобы кто-то вводил адрес электронной почты из 200 символов с комментариями, новыми строками, цитатами, пробелами, круглыми скобками или иным бредом при подписке на список рассылки, информационный бюллетень или веб-сайт? Правильный ответ для таких клоунов: «Возвращайся позже, когда у тебя будет адрес, похожий на username@domain.tld».

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

Да, это означает, что я отклоняю действительные адреса, по которым кто-то может попытаться зарегистрироваться на моих веб-сайтах - возможно, я «неправильно» отклоняю до 0,001% реальных адресов! Я могу жить с этим.

4 голосов
/ 01 октября 2008

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

Что касается действительного регулярного выражения для него, модуль Perl Mail :: Rfc822 :: Address содержит регулярное выражение, которое, очевидно, будет работать - но только если какие-либо комментарии уже были заменены пробелами. (Комментарии в адресе электронной почты? Вы понимаете, почему это сложнее, чем можно было ожидать ...)

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

3 голосов
/ 01 октября 2008

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

3 голосов
/ 06 января 2009

Простой и хороший способ проверить адреса электронной почты в Java - использовать EmailValidator библиотеки Apache Commons Validator .

Я бы всегда проверял адрес электронной почты в форме ввода на предмет чего-то подобного перед отправкой электронного письма - даже если вы только поймаете некоторые опечатки. Вы, вероятно, не хотите писать автоматический сканер для уведомлений о «доставке не удалось». : -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...