Прекратите проверять Email с помощью регулярных выражений!

в 21:12, , рубрики: email, regex, валидация, переводы, Программирование, Регулярные выражения

Серьезно, прекратите. Это пустая трата времени и сил. Поищите регулярку для проверки Email в Google, взгляните на нее — и захочется отойти подышать свежим воздухом. Вспоминается одна очень известная цитата:

Некоторые люди, сталкиваясь с проблемой, думают: «О, я воспользуюсь регулярными выражениями».
Теперь у них две проблемы.

Джэйми Завински, regex.info


Вот довольно часто встречающийся пример кода из приложения на Rails, содержащий некоторое подобие системы авторизации:

class User < ActiveRecord::Base
  # Эта регулярка взята из проекта from https://github.com/plataformatec/devise,
  # самой популярной библиотеки авторизации для Rails
  validates_format_of :email, :with => /A[^@]+@([^@.]+.)+[^@.]+z/
end

Выглядит довольно просто (разве что если вы совсем не знаете регулярных выражений), но бывает и сильно хуже:

class User < ActiveRecord::Base
  validates_format_of :email, :with => /^(|(([A-Za-z0-9]+_+)|([A-Za-z0-9]+-+)|([A-Za-z0-9]+.+)|([A-Za-z0-9]+++))*[A-Za-z0-9]+@((w+-+)|(w+.))*w{1,63}.[a-zA-Z]{2,6})$/i
end

Или совсем плохо:

class User < ActiveRecord::Base
  validates :email, :with => EmailAddressValidator
end

class EmailValidator < ActiveModel::Validator
  EMAIL_ADDRESS_QTEXT           = Regexp.new '[^\x0d\x22\x5c\x80-\xff]', nil, 'n'
  EMAIL_ADDRESS_DTEXT           = Regexp.new '[^\x0d\x5b-\x5d\x80-\xff]', nil, 'n'
  EMAIL_ADDRESS_ATOM            = Regexp.new '[^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+', nil, 'n'
  EMAIL_ADDRESS_QUOTED_PAIR     = Regexp.new '\x5c[\x00-\x7f]', nil, 'n'
  EMAIL_ADDRESS_DOMAIN_LITERAL  = Regexp.new "\x5b(?:#{EMAIL_ADDRESS_DTEXT}|#{EMAIL_ADDRESS_QUOTED_PAIR})*\x5d", nil, 'n'
  EMAIL_ADDRESS_QUOTED_STRING   = Regexp.new "\x22(?:#{EMAIL_ADDRESS_QTEXT}|#{EMAIL_ADDRESS_QUOTED_PAIR})*\x22", nil, 'n'
  EMAIL_ADDRESS_DOMAIN_REF      = EMAIL_ADDRESS_ATOM
  EMAIL_ADDRESS_SUB_DOMAIN      = "(?:#{EMAIL_ADDRESS_DOMAIN_REF}|#{EMAIL_ADDRESS_DOMAIN_LITERAL})"
  EMAIL_ADDRESS_WORD            = "(?:#{EMAIL_ADDRESS_ATOM}|#{EMAIL_ADDRESS_QUOTED_STRING})"
  EMAIL_ADDRESS_DOMAIN          = "#{EMAIL_ADDRESS_SUB_DOMAIN}(?:\x2e#{EMAIL_ADDRESS_SUB_DOMAIN})*"
  EMAIL_ADDRESS_LOCAL_PART      = "#{EMAIL_ADDRESS_WORD}(?:\x2e#{EMAIL_ADDRESS_WORD})*"
  EMAIL_ADDRESS_SPEC            = "#{EMAIL_ADDRESS_LOCAL_PART}\x40#{EMAIL_ADDRESS_DOMAIN}"
  EMAIL_ADDRESS_PATTERN         = Regexp.new "#{EMAIL_ADDRESS_SPEC}", nil, 'n'
  EMAIL_ADDRESS_EXACT_PATTERN   = Regexp.new "\A#{EMAIL_ADDRESS_SPEC}\z", nil, 'n'

  def validate(record)
    unless record.email =~ EMAIL_ADDRESS_EXACT_PATTERN
      record.errors[:email] << 'is invalid'
    end
  end
end

Ага. Неужели действительно нужно использовать нечто настолько сложное? Если перейти по ссылке в начале статьи, вы увидите, что люди уже многие годы пишут (или пытаются написать) регулярки для проверки email-адреса, которые бы соответствовали описанию RFC. Некоторые из них оказываются просто до смешного заумными, как в последнем примере, и все равно не пропускают некоторые корректные адреса.

О том, какой email-адрес является корректным, написано в разделах 3.2.4 и 3.4.1. Там сказано, что при наличии обратного слэша и кавычек остается не так уж много вещей, которые нельзя использовать в адресе. Локальная часть адреса (та строка, что идет перед символом @), может содержать следующие символы:

! $ & * - = ^ ` | ~ # % ' + / ? _ { }

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

"Look at all these spaces!"@ example.com

Прекрасно!

По этой причине, с недавнего времени я проверяю все email-адреса следующим регулярным выражением:

class User < ActiveRecord::Base
  validates_format_of :email, :with => /@/
end

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

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

Просто пошлите пользователю его письмо!

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

Представьте себе такой сценарий. Я регистрируюсь на вашем сайте под следующим адресом:

qwiufaisjdbvaadsjghb@gmail.com

Да ладно вам! С этой хренью ни один почтовый демон работать не станет, но форматирование в полном порядке: это же валидный email-адрес! Для решения данной проблемы вы пишете систему, которая после регистрации отправляет мне email со ссылкой, по которой я должен перейти. Это требуется для того, чтобы удостовериться, что я действительно имею доступ к почтовому ящику, на который регистрируюсь. В таком случае, зачем проверять формат адресов? Результат отправки письма на неправильный адрес будет точно такой же — письмо не примет сервер. Если пользователь ввел некорректный адрес, он не получит письмо и попытается зарегистрироваться на вашем сайте еще раз, если ему это и правда нужно. Вот и все.

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

Если же вы все равно не можете успокоиться, пока не проверите адрес на корректность, просто проверьте на наличие в нем символа @. А если чувствуете, что способны на большее — добавьте проверку на точку:

/.+@.+..+/i

Все, что сверх этого — стрельба из пушки по воробьям.

Примечание переводчика:
Ссылку на эту статью нашел в комментарии к другому переводу. Спасибо jetman!

Автор: impwx

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js