Точная проверка Email адреса регулярным выражением

    Как все знают, один из самых удобных способов проверки e-mail адреса является регулярные выражения. Недавно пришлось столкнулся с проблемой максимально точной проверки адресов. Данная проверка была необходима в системе автоматической рассылки спама опросников, где каждый список адресов подгружался автоматически одним большим файлом. Требовалось исключить максимальное количество заведомо невалидных адресов.
    Проблема заключалась в том, что все шаблоны проверки е-мэйла, которые можно встретить в интернете, МСДН и других источниках не удовлетворяли требованиям проверки. Обратившись к первоисточникам в виде RFC 2821 и RFC 2821, я выяснил как же точно и правильно валидирвоть адреса.


    Е-майл адрес = local part @ domain part

    Local part



    Символы разрешенные в local part:
    • +
    • -
    • . ( кроме случаев local..part — две точки подряд, .localPart — точка вначале и localPart. – точка в конце )
    • 0-9
    • A-Z, a-z
    • -


    Символы НЕ разрешенные в local part
    • !
    • #
    • $
    • %
    • (
    • )
    • ,
    • :
    • ;
    • <
    • >
    • [
    • \
    • ]
    • |
    • SPACE, DEL, Control chars


    Символы, которые нежелательно использовать в local part, но которые могут присутствовать. (Требуют тестирования, принимает ли их сервер).
    • ?
    • *
    • /
    • =
    • ?
    • ^
    • {
    • }
    • ~

    Причина, по которой не стоит их использовать в адресах это то, что многие относятся к группе символов «UNIX shell special characters».

    Domain part



    — может быть, либо в виде IP адреса, IP-адреса с портом либо просто литерального выражения содержащего только строчные и прописные латинские буквы и символ черточки ( ‘-‘, но есть ограничение: черточка не может быть, ни в конце, ни в начале; по поводу ограничения на две черточки подряд ничего не сказано), разделенные точками. Соответственно выражение domain..com – невалидное.

    В итоге модифицировав один из интернетовских шаблонов получил:

    ^[a-zA-Z0-9_'+*/^&=?~{}\-](\.?[a-zA-Z0-9_'+*/^&=?~{}\-])*\@((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\:\d{1,3})?)|(((([a-zA-Z0-9][a-zA-Z0-9\-]+[a-zA-Z0-9])|([a-zA-Z0-9]{1,2}))[\.]{1})+([a-zA-Z]{2,6})))$

    Ссылки:
    RFC 2821: www.remote.org/jochen/rfc/rfc821.txt
    RFC 2822: www.remote.org/jochen/rfc/rfc822.txt

    Список валидных/невалидных символов: www.remote.org/jochen/mail/info/chars.html

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

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 57

      –3
      Еще бы все это под кат, и вообще шикарно было бы
        –3
        Не видел ваш коммент, но присоединяюсь камментом ниже.
        –3
        Хабракат пожалуйста.
          +5
          внимательно читаем RFC

          <local-part> ::= <dot-string> | <quoted-string>
          <quoted-string> ::= """ <qtext> """
          <qtext> ::= "\" <x> | "\" <x> <qtext> | <q> | <q> <qtext>

          где у вас обраотка quoted-string?

          правильный регэксп занимаем почти страницу
            +11
            Правильный регэксп лежит здесь. Там даже если заподозришь, что есть неточности — фиг проверишь :)
              0
              paste.ly/K
              вот его сокращенная для читаемости версия
                –1
                Да, тестили мы правильный регекс, несколько тестов не прошло.
                *уже ищу, где же эти тесты. если найду — выложу.
                  0
                  кажется, это ----@--.--
                    0
                    См. мой ответ ниже. По RFC это корректный адрес.
                  +4
                  О раз искал этот регэксп, спасибо :)
                  А на пхп лучше делать так filter_var('mylo@mail.ru', FILTER_VALIDATE_EMAIL)
                    0
                    Согласен! К тому же, думаю если необходимо будет проверять кучу мыл, то по-быстрее будет.
                    –2
                    Это неправильный регэксп. Он, в частности, пропускает подчеркивание в доменной части.
                      0
                      domain      =  sub-domain *("." sub-domain)
                      sub-domain  =  domain-ref / domain-literal
                      domain-ref  =  atom
                      atom        =  1*<any CHAR except specials, SPACE and CTLs>
                      CTL         =  <any ASCII control           ; (  0- 37,  0.- 31.)
                                      character and DEL>          ; (    177,     127.)
                      SPACE       =  <ASCII SP, space>            ; (     40,      32.)
                      specials    =  "(" / ")" / "<" / ">" / "@"  ; Must be in quoted-
                                       /  "," / ";" / ":" / "\" / <">  ;  string, to use
                                       /  "." / "[" / "]"              ;  within a word.

                      RFC822.
                        0
                        Что это Вы тут показали? Поясните, пожалуйста.
                          +1
                          domain состоит из sub-domain, который может быть domain-ref. domain-ref это atom.

                          atom состоит из любых ASCII символов, кроме specials, SPACE и CTL. Подчёркивание в эти множества не входит, соответственно atom может содержать подчёркивания.
                            0
                            RFC822 — это устаревший RFC. Более актуален RFC2822
                            www.faqs.org/rfcs/rfc2822.html
                            Правда, содержимое RFC2822 читается так, что в домене получаются разрешенными вообще поголовно все символы, что еще более запутывает дело.

                            При этом, однако, есть более авторитетный в вопросах именования доменов RFC952
                            www.ietf.org/rfc/rfc952.txt
                            в котором русским по белому сказано:

                            >A «name» (Net, Host, Gateway, or Domain name) is a text string up
                            >to 24 characters drawn from the alphabet (A-Z), digits (0-9), minus
                            >sign (-), and period (.).

                            Ну и в других связанных RFC подчеркивание в домене запрещено.

                            Плюс, истина в последней инстанции BIND считает, что подчеркивания в домене быть не должно.

                            В общем, допускать подчеркивание в email'e не следует ни в коем разе.
                              0
                              А как насчёт того, что MTA может производить rewriting почтовых адресов, и поэтому domain не обязан быть реально существующим доменом?
                      0
                      Потянет на кандидатскую в сфере IT :-)
                    0
                    А где ограничители регулярного выражения и модификаторы (если нужны)?
                      0
                      И что, нет никаких ограничений по длинне доменного имени?
                        0
                        А зачем?
                        У меня например есть qwertymegamailqwerty@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com
                        Я его конечно для фана зарегал, но провайдер предупреждает что могу возникнуть проблемы у некоторых.
                          0
                          Ну да, всё верно, потому что максимальная длина доменного имени согласно RFC 1035 составляет 63 символа.
                          Именно поэтому последняя буква в вашем домене k, 63-я по счету
                        –1
                        000@000.000.000.000 в представлении вашего регэкспа — нормальный email
                          0
                          А чем он плох?
                            0
                            ну как бы на ip это не очень похоже, и на домен не тянет
                          0
                          низачот.
                          Если нужна максимальная точность — достаточно простого регекспа.
                          Но после него в обязательном порядке должна идти функция проверки полученных данных.

                          Она может использовать как проверку существоания почтового сервера, к примеру на основе этой функции ru2.php.net/manual/en/function.dns-get-mx.php

                          Так и проверку на существование почтового адреса — отправка на него «секретной» ссылки с предложением подтвердить существование ящика.
                            –1
                            Вот тут я могу с вам поспорить. Это касалось специфики проекта. Дело в том, что наш клиент брал монету с клиента за каждый адрес, на который было отправлено приглашение (ссылка на опрос). Поэтому предварительная проверка в виде «секретной» ссылки не подходит.

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

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

                                  Можно погуглить в эту сторону:
                                  www.google.com/search?hl=ru&client=iceweasel-a&rls=org.mozilla%3Aru%3Aunofficial&q=SMTP+VERIFY&btnG=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA&lr=lang_ru&aq=f&oq=

                                  Но не все почтовые сервисы поддерживают такую команду.
                                    0
                                    Прошу прощения, думал это ссылка на сервис (которые мы тоже рассматривали как вариант).

                                    Что касается функции протокола — мы рассматривали и это тоже и как раз из-за того что не все почтовые сервисы поддерживают такую команду — отказались.
                                      +2
                                      я промолчу что регексп не гарантирует ничего :)
                            –1
                            Для «серьёзной и точной» проверки использование ([a-zA-Z]{2,6}) для TLD — несерьёзно. Даже если не перечислять все двухбуквенные домены, уже получится что-то подобное:
                            (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])
                              0
                              Ужас. А что в качестве доменной части указать что-то типа 77.88.0x15.010 yельзя уже? Вполне себе коректная domain part.

                              Да и убивать нужно за такой хард код. Вы на каждый введенные домен первого уровня будите в код лазить менять?
                                –1
                                IP-адреса в доменной части обрабатываются отдельно (см. регэкс автора). Локальные емейлы типа «director@corporate.lan» для большинства интернет-проектов неактуальны (корпоративные — отдельная тема). Оставшийся вариант будет содержать тот или иной TLD из списка ICANN.

                                Претензии насчёт хард-кода не понял. Как вы по одному выражению определили, что оно записано в коде программы, а не в конфигурации?
                                  0
                                  Естественно этот регексп — часть конфигурации. Как и откуда брать файл, тип файла, и Sheet/Column если это экселевский файл.
                                    0
                                    Коде программы или в конфигурации, суть не меняется.

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

                                    Вот смотрите — вы допустим сделаете это условия. Значит в описании функции в документации вам нужно указать — все текущие домены которые она поддерживает, опистаь как и где поменять и добавить текущий домен. Для добавления вам нужно поменить код(конфигурационный файл), документацию и прочее, прочее.

                                    И зачем? Что это даст?
                                      +2
                                      Задача была
                                      Исключить максимальное количество заведомо невалидных адресов.
                                      Например, адрес mail@mail.mail — заведомо невалидный. Выражение автора его пропустит, а проверка по существующим доменам первого уровня — забракует.

                                      Да, на поддержание актуальности списка потребуются дополнительные усилия (к счастью, список актуальных TLD меняется далеко не каждый месяц). В некоторых случаях эти усилия оправданы, в некоторых — нет. Для вышеприведённой задачи — оправданы.
                                • UFO just landed and posted this here
                                  0
                                  Кто-то мне сможет ответить в чем смысл настолько дотошной проверки e-mail'а?
                                    0
                                    Судя по отсутствию ответов понял что это искусство ради любви к искусству. Что же, тоже неплохо :)
                                      0
                                      Хотелось бы подчеркнуть – что это частный случай в котором понадобилось использование серьезной и точной проверки (как требовал клиент). В иных случаях можно не заморачиваться :)

                                      Последние строки статьи
                                    0
                                    Mail::RFC822::Address: regexp-based address validation

                                    www.ex-parrot.com/~pdw/Mail-RFC822-Address.html
                                      0
                                      Самая точная проверка email'a — отправьте на него сообщение вида:
                                      «Для подтверждения эл.почты перейдите по ссылке»
                                        0
                                        Так Вы подтвердите не только существование ящика, но и желание получать от вас письма :)
                                          0
                                          Уже вижу, как приходит письмо — «Примите участие в опросе. Для подтверждения — перейдите по этой ссылке. После этого вам придет письмо с сылкой на опрос.» :)
                                          Ребята, читайте комент выше. :)
                                          +9
                                          Ох уж мне эти сказочники… :-)
                                          Тут автору уже указали на многие недочёты. Могу добавить, что автор не учёл комментарии (см RFC). Но дело даже не в этом. Автор, почитайте что-нибудь по регекспам (рекомендую Фридла), в любой хорошей книжке вам напишут, что нельзя написать регексп, разбирающий e-mail в полном согласии с RFC (e-mail — это классический пример задачи, неразрешимой с помощью регекспов). Вернее, такой регексп написать можно, но только используя рекурсию, а при этом регексп становится небезопасным, так как можно подсунуть ему такие даные, что он начнёт жрать слишком много ресурсов или даже будет циклиться и виснуть.
                                          Вот популярное объяснение, почему if you need a 100% rock solid check for e-mail, don't use a regex.
                                            +4
                                            спасибо за информацию, учту!

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

                                            спасибо.
                                            0
                                            А айпишники с числами более 255 тоже проходят?
                                            А где проверка номера порта?
                                            А если айпишник IPv6?
                                              +1
                                              И вообще бедный заказчик… получит абсолютно не читаемый и не поддерживыемый код.

                                              Ответьте — вот если бы вы сами получили бы такой кусок кода и просьбу проверить корректность работы? Думаеться первое чтобы вы сделали это бы выкинули его и написали новый с нуля.

                                              Есть все же мнение что для больших проектов использование регэкспов в таком вот виде нужно запрещать.
                                                0
                                                Ваши предложения? Написать без регекспа?
                                                  +1
                                                  Да, я бы как заказчик или как code support предпочел бы получить код виде явно реализованного конечного автомата.

                                                  В крайнем случае в виде ряда отдельных функций, как минимум фукция выделения local part и domain part и две функции проверки local и domain парт.
                                                  Внутри них подфункции проверки отдельных условий по RFC с указанием в комментариях какое именно условие проверяет эта функция.

                                                  Получиться больше кода, но зато он будет поддерживаемым и читабельным. Что на мой взгляд сильно важнее чем размер исходников.
                                                    0
                                                    Безусловно вы правы. К сожалению, как я ответил каментом выше — это всего лишь часть конфигурационного файла. Написать собственный код у меня не было возможности. Уж простите.
                                                0
                                                A-Z, a-z — ну фиг знает, домены перестают быть только в латинице, как и мейлы.
                                                  0
                                                  Мне кажется, если очень хочется сделать формально правильную проверку, надо использовать парсер.
                                                  Точно не скажу что подойдёт для BNF, но есть подозрение что любой парсерогенератор.
                                                    0
                                                    Откройте вами же приведенную ссылку и внимательно прочтите пункт 6.1. Когда же, наконец, писатели регулярных выражений узнают, что remal <mail@domain.com> является валидным мылом?
                                                      +2
                                                      Когда я прочитал название топика (в «Прямом эфире»), я подумал, что зайдя сюда, найду в качестве содержимого одно слово: «Невозможна»… ну или по крайней мере что-то в таком духе.

                                                      Кстати, даже если не касаться конкретно e-mail адресов, известно, что «если у вас есть проблема и вы собрались использовать для её решения регулярные выражения, то теперь у вас две проблемы» :)

                                                      Only users with full accounts can post comments. Log in, please.