Pull to refresh

Валидация email

Reading time3 min
Views157K
В этой статье рассмотривается валидация email изпользуя регулярные выражения. Все регэкспы выполняются с модификатором i, т.е. делают регистронезависимую проверку.

Подготовка


Перед тем как писать валидацию, надо знать из чего состоит email адрес. Думаю известно всем что это «username@hostname». Лучше всего будет разбить создание регэкспа на 2 логические части — валидация hostname и валидация username. Начнём с более объёмного.

Валидация hostname


Для начала подумаем, а из чего же состоит hostname?

Имя хоста состоит из нескольких компонентов, разделённых точкой и не превышающих 63 символа, и суффиксов (домены первого уровня). Компоненты, в свою очередь, состоят из латинских букв, цифр и дефисов, причём дефисы не могут быть в начале или в конце компонента. Суффиксы это ограниченный список доменов первого уровня (я нашёл список на сайте IANA). Для упрощения выражения запишем домены стран как [a-z][a-z] (любые 2 символа от a до z не зависимо от регистра). Так же не будем использовать не латинские символы, пока они официально не введены в публичное пользование. В итоге получим выражение проверяющее суффикс (конструкция (foo|bar) говорит о том что происходит поиск либо foo либо bar, т.е. заменяет или):

(aero|arpa|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|[a-z][a-z])

Для компонентов код будет посложнее:

([a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?\.)

Разберём выражение:

[a-z0-9] # обязательная буква латинского алфавита или цифра
([-a-z0-9]{0,61}[a-z0-9])? # необязательная часть
\. # точка


Рассмотрим необязательную часть:

# дефис ставиться на первое место в группе символов, иначе он принимается за промежуток
# {0,61} после группы символов означает, что группа может повторятся от 0 до 61 раза
[-a-z0-9]{0,61}
# 61 для того, чтобы в группе не было более 63 символов в сумме
[a-z0-9]


В итоге мы получили выражение отвечающее за проверку hostname:

([a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?\.)*(aero|arpa|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|[a-z][a-z])

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

Валидация username


Имя пользователя может в себе содержать:
  • латиницу
  • цифры
  • знаки! # $ % & ' * + — / =? ^ _ ` { | } ~
  • точку, за исключением первого и последнего знака, которая не может повторятся

Приведу сразу выражение:

[-a-z0-9!#$%&'*+/=?^_`{|}~]+(\.[-a-z0-9!#$%&'*+/=?^_`{|}~]+)*

На самом деле тут все просто: 1 или более символ [-a-z0-9!#$%&'*+/=?^_`{|}~], далее 0 или больше компонентов \.[-a-z0-9!#$%&'*+/=?^_`{|}~]+.

В итоге


Регэксп проверки email:

^[-a-z0-9!#$%&'*+/=?^_`{|}~]+(\.[-a-z0-9!#$%&'*+/=?^_`{|}~]+)*@([a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?\.)*(aero|arpa|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|[a-z][a-z])$

Это выражение можно каплю оптимизировать (про оптимизацию, я думаю, будет отдельная статья):

^[-a-z0-9!#$%&'*+/=?^_`{|}~]+(?:\.[-a-z0-9!#$%&'*+/=?^_`{|}~]+)*@(?:[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?\.)*(?:aero|arpa|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|[a-z][a-z])$

Бонус


Рассмотрим регэксп, который привели в пример в комментарии к вступительному топику:

^(\S+)@([a-z0-9-]+)(\.)([a-z]{2,4})(\.?)([a-z]{0,4})+$

Какие тут есть главные проблемы?
  • Так как username может состоять из любых символов кроме пробелов, username "日本国" является валидным.
  • Домен первого уровня может состоять из любых 4 латинских букв, например .habr
  • Ужасная проверка домена: 1 или более символов, обязательная точка, 2-4 обязательных символа, необязательная точка, 0-4 символа в прибавок. Причём каждый из этих блоков сохраняется в память.


P.S. Пишите о багах и пожеланиях — обязательно исправлю.
P.P.S. По желанию могу выложить функцию проверки email, используюмую мной.
Tags:
Hubs:
Total votes 100: ↑64 and ↓36+28
Comments122

Articles