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

Мы все знаем, что регулярное выражение для правильной проверки электронной почты будет довольно сложным. Однако плагин проверки jQuery имеет более короткое регулярное выражение (внесенное Scott Gonzalez), охватывающее всего несколько строк:

/^((([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])))\.?$/

Почему это так "просто" по сравнению с более известным чудовищем? Существуют ли случаи, когда одно регулярное выражение терпит неудачу, а другое будет успешным (допустимы ли случаи или недействительные электронные письма)?

Ответ 1

Регулярное выражение представляет собой пользовательскую комбинацию:

  • RFC 2234 ABNF
  • RFC 2396 Общий синтаксис URI (обремененный RFC 3986)
  • RFC 2616 Протокол передачи гипертекста - HTTP/1.1
  • Формат сообщений в формате RFC 2822
  • RFC 3987 IRI
  • RFC 3986 Общий синтаксис URI

Я написал регулярное выражение при создании Web Forms 2.0 и RFC 5322 не существовало. Если вы посмотрите на порядок, в котором были написаны RFC, вы заметите, что определение IRI и URI изменилось после того, как был создан формат сообщений Интернета. Это означает, что RFC 2822 не поддерживает текущие определения IRI. К сожалению, это была не простая задача только подстановки определений, поэтому мне пришлось выбирать, какие определения использовать из RFC. Я также сделал выбор о том, что удалить (например, поддержка комментариев).

Регулярное выражение не полностью написано вручную. Хотя я вручную записывал каждый раздел регулярного выражения, я написал сценарий "клей". Каждое определение из RFC хранится в переменной с составными определениями, использующими переменные, которые хранят более простые определения (@Walf: вот почему так много подшаблонов и орлов).

Чтобы усложнить этот вопрос, версия регулярного выражения, которая используется в плагине проверки jQuery, еще более изменена, чтобы учитывать различия между допустимыми адресами и ожиданием пользователя действительного адреса. Я не помню, какие изменения я сделал. Я пообещал Jörn Zaefferer (автора плагина проверки), что я бы написал новый script для генерации регулярного выражения. Новый script позволит вам указать параметры того, что вы делаете и не хотите поддерживать (требуемые TLD, конкретные TLD, IPv6, комментарии, устаревшие ошибки, цитируемые локальные имена и т.д.). Это было 5 лет назад. Я начал его один раз, но так и не закончил. Возможно, однажды я это сделаю. До сих пор я размещен на GitHub: https://github.com/scottgonzalez/regex-builder

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

/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-][email protected][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])?)*$/

Если вы используете regex-builder и отключите все параметры, вы получите что-то подобное. Но прошло около года с тех пор, как я посмотрел на него, поэтому я не помню, каковы различия.


Я также хотел бы отметить, что ссылка в оригинальном вопросе специально упоминает RFC 822. Хотя это здорово, что RFC 822 продвинул нас из Arpanet в интернет-ARPA, это не совсем актуально. За последние три десятилетия Интернет сделал несколько успехов, и этот RFC был заменен дважды. Я бы хотел увидеть новую работу, соответствующую последним стандартам.


UPDATE:

Друг спросил меня, почему регулярное выражение HTML5 не поддерживает UTF-8. Я никогда не спрашивал об этом Хикси, но я полагаю, что это причина: хотя некоторые TLD начали поддерживать IDN (международные доменные имена) в 2000 году, а RFC 3987 (IRI) был написан в 2005 году, когда RFC 5322 был написан в 2008 году он перечисляет только символы в диапазонах 33-90 и 94-126 как допустимые dtext (символы разрешены для использования в литературе домена). HTML5 основан на RFC 5322, и в результате нет поддержки UTF-8. Кажется странным, что RFC 5322 не учитывает IDN, но ничего не стоит того, что даже в 2008 году IDN не использовались на практике. Только в 2010 году ICANN утвердила первый набор IDN. Однако даже сегодня, если вы хотите использовать IDN, вам в значительной степени нужно полностью уничтожить доменное имя с помощью Punycode, если вы действительно хотите, чтобы такие вещи, как электронная почта и DNS, работали глобально.

ОБНОВЛЕНИЕ 2:

Обновлено регулярное выражение HTML5 в соответствии с обновленной спецификацией, которая изменила ограничения длины ярлыков от 255 символов до 63 символов, как указано в RFC 1034, раздел 3.5.

Ответ 2

Это не выглядит правильно: что с Unicode? Какой RFC подтверждает это?

См. этот ответ для правильного регулярного выражения RFC5322.