Проверка формата адреса email без регулярных выражений

Описано решение проверки формата адреса email без использования скриптов и регулярных выражений, только кодом 1С. Применено в конфигурации УТ11.

Итак, ситуация: на предприятии активно используются уведомления e-mail. Конфигурация УТ11, учетная запись 1С подключена к SMTP, письма отправляются регламентным заданием. Если в каком-то из писем указан неверный по формату адрес получателя (например, скопированный из Word и заканчивающийся «переводом строки») — SMTP возвращает ошибку, но 1С её обрабатывать на стадии отправки не умеет (ИнтернетПочта.Послать()). Соответсвенно в журнале регистрации ничего не зафиксировано, регламентное задание успешно работает, но письма перестают отправляться.

Проблема проверки адреса конечно не нова, но обычно предлагается решения с проверкой адреса через регулярные выражения, используя VBScript (RegExp) или Java. Но погрузившись немного в тему, я пришел к выводу что более правильный способ предлагается в http://www.lexpr.ru/node/382.

В кратце — алгоритм проверяет адрес на соответствие RFC 1035 “Domain Implementation and Specification”, RFC 2234 “ABNF for Syntax Specifications”, RFC 2821 “Simple Mail Transfer Protocol”, RFC 2822 “Internet Message Format”. Правда, выяснилось что «жизнь шире наших схем» и домен 1C, например, им не соответсвует 🙂

Ну и собственно реализовал некий неполный аналог (алгоритма предложенного автором статьи по ссылке) в виде функции в общем модуле в 1С.

Применительно к УТ11 данная проверка дополнительно к штатным исользуется перед записью контактной информации в

УправлениеКонтактнойИнформацией.ЗаполнитьРеквизитыТабличнойЧастиДляАдресаЭлектроннойПочты()

и перед отправкой письма в функции

ЭлектроннаяПочта.ОтправитьСообщение()

при формировании адреса получателя. После работы типового кода каждый идентифицированный адрес проверяется указанной функцией и удаляется если не проходит проверку; если в результате не остается ни одного получателя — вызывается исключение.

Соответсвенно происходит переход к отправке следующего письма и запись об ошибке в журнале регистрации.

Код проверочной функции:

Функция EmailValid(Адрес) Экспорт

    //Адрес = «test@me@gmail.narod.am»;

    ЛатинскиеБуквы = «abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ»;

    Цифры = «0123456789»;

    //ищем крайний справа символ @ для правильного выделения локальной и доменной части

    ИндексСобаки = Найти(Адрес,«@»);

    //1. строка адреса вообще не содержит разделителя

    Если ИндексСобаки = 0 Тогда

        Возврат ЛОЖЬ;

    КонецЕсли;

    УрезаемаяСтрока = Сред(Адрес, ИндексСобаки+1);

    Пока Найти(УрезаемаяСтрока,«@») > 0 Цикл

        ИндексСобаки = ИндексСобаки + Найти(УрезаемаяСтрока,«@»);

        УрезаемаяСтрока = Сред(УрезаемаяСтрока, ИндексСобаки+1);

    КонецЦикла;

    ДоменнаяЧасть = Сред(Адрес, ИндексСобаки+1);

    ЛокальнаяЧасть = Лев(Адрес, ИндексСобаки1);

    //2. Проверяем длину локальной части

    Если СтрДлина(ЛокальнаяЧасть) < 1 ИЛИ СтрДлина(ЛокальнаяЧасть) > 64 Тогда

        Возврат ЛОЖЬ;

    КонецЕсли;

    //3. Проверяем длину доменной части

    Если СтрДлина(ДоменнаяЧасть) < 1 ИЛИ СтрДлина(ДоменнаяЧасть) > 255 Тогда

        Возврат ЛОЖЬ;

    КонецЕсли;

    //4. Проверяем что локальная части не начинается и не заканчивается на «.»

    Если Лев(ЛокальнаяЧасть, 1) = «.» ИЛИ Прав(ЛокальнаяЧасть, 1) = «.» Тогда

        Возврат ЛОЖЬ;

    КонецЕсли;

    //5. Локальная части не содержит 2 или более «.» подряд

    Если Найти(ЛокальнаяЧасть, «..») > 0 Тогда

        Возврат ЛОЖЬ;

    КонецЕсли;

    //Проверка доменной части

    //6. Доменная часть не начинается с точки

    Если Лев(ДоменнаяЧасть, 1) = «.» Тогда

        Возврат ЛОЖЬ;

    КонецЕсли;

    //7. Доменная часть не содержит 2 или более «.» подряд

    Если Найти(ДоменнаяЧасть, «..») > 0 Тогда

        Возврат ЛОЖЬ;

    КонецЕсли;

    //8. Проверка частей доменной части

    //каждая часть начинается с буквы и заканчивается буквой или цифрой

    //каждая часть длиной не более 63 символов

    ИдентификаторыДоменнойЧасти = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(ДоменнаяЧасть, «.»);

    Для Каждого ИдентификаторДомена ИЗ ИдентификаторыДоменнойЧасти Цикл

        Если СтрДлина(ИдентификаторДомена) > 63 Тогда

            Возврат ЛОЖЬ;

        КонецЕсли;

        Если Найти(ЛатинскиеБуквы, Лев(ИдентификаторДомена,1)) = 0

            //для доменов, нарушающих RFC 1035 п.2.3.1, например @1c.ru 🙂

            И Найти(Цифры, Лев(ИдентификаторДомена,1)) = 0

            Тогда

            Возврат ЛОЖЬ;

        КонецЕсли;

        Если Найти(ЛатинскиеБуквы, Прав(ИдентификаторДомена,1)) = 0 И Найти(Цифры, Прав(ИдентификаторДомена,1)) = 0 Тогда

            Возврат ЛОЖЬ;

        КонецЕсли;

    КонецЦикла;

    //Все проверки пройдены — радуемся

    Возврат ИСТИНА;

КонецФункции

36 Comments

  1. aet

    ЛокальнаяЧасть не проверяется на Не ЛатинскиеБуквы, а вот ДоменнаяЧасть, по-моему, может содержать Не ЛатинскиеБуквы.

    Reply
  2. ShantinTD

    А скорость работы?

    Чем не устроили регулярные выражения?

    Reply
  3. Поручик

    (2) Очевидно тем, что регулярки чуждые элементы в организме 1С Предприятия. Ну и работать в 1С они могут только в windows среде.

    Reply
  4. ShantinTD

    (3) Поручик, возражаю. Берем RexV8 и пользуемся почти что родной библиотекой (отвечает в 1С за Юникод) на любой системе (виндоус или линукс).

    Reply
  5. Vlad_M

    (2) ShantinTD, насколько я понял не написано еще такого выражения которое бы корректно пропускало некоторые допустимые адреса 🙂 http://habrahabr.ru/post/175375/

    Адреса: Abc@def@example.com, customer/department=shipping@example.com и !def!xyz%abc@example.com с точки зрения RFC все являются правильными, но отвергнутся 90% регулярок.

    Ну и действительно я о RexV8 не знал.

    По поводу скорости работы — это не самое медленное место в УТ11 🙂

    Reply
  6. HEKPOH

    1. По скорости описанный механизм проигрывает в 2 раза регулярам

    2. «…регулярки чуждые элементы в организме 1С Предприятия…» Наверно, такие же чуждые, как и внешние компоненты)))

    Reply
  7. Yashazz

    Насчёт чуждых — это хорошая отмазка пользоваться кривыми медленными велосипедами, вместо чтобы сделать по-человечески, да ещё наработанные ранее паттерны использовать…

    Очень ждём родной объект — «Новый РегулярноеВыражение».

    Reply
  8. Vlad_M

    (6)По поводу скорости — внутри отправки электронного письма исходящего из УТ11, даже если такая проверка в 2 раза медленнее регулярки — на время выполнения всей функции отправки влияния не зафиксировано. Если процесс длится 100 единиц, в него добавляют 2 варианта проверки — быстрый за 1 единицу и медленный за 2 — то это не критерий.

    (7) по поводу наработанных паттернов — вновь про примеры корректных адресов, которые ими отвергаются Abc@def@example.com, customer/department=shipping@example.com и !def!xyz%abc@example.com

    Reply
  9. ShantinTD

    1. Интересно посмотреть на «стандарт» относительно адресов электронной почты.

    2. Интересно узнать: какой процент поставщиков услуг электронной почты (назовем их так) позволяет использовать исключительно корректные адреса?

    К размышлению: в HTML5 определены поля ввода в том числе с типом email. То есть не просто текст, а именно адрес электронной почты. И есть проверка на правильность (валидность). Так вот каждый браузер сам реализует поддержку стандарта HTML5, и у каждого браузера получается разный результат при проверке одного и того же адреса. Причем так: некорректный адрес некоторыми браузерами определяется как корректный, и наоборот. (Сейчас уже не вспомню конкретно по браузерам, но в 2011 году проходил обучение — тестировал во всевозможных комбинациях).

    (7), действительно «очень ждем».

    А говорить, что «и так сойдет, все равно медленно работает» — неправильно. Просто в некоторых масштабах позволительно (?) не замечать потерь производительности.

    Reply
  10. SeiOkami

    Лучше чуть медленнее, но родными средствами!

    Так что спасибо =)

    Reply
  11. ShantinTD

    (10) SeiOkami, в «два раза» это не «чуть медленнее»…

    Пример: кто-то втиснул в самописную конфу алгоритм расчета МД5. Родными средствами. То есть совсем родными — на встроенном языке. Ничего, что в 100 раз (!!! не шутка, без преувеличения) медленнее, чем внешними средствами. На 8.3 поправил, конечно, но «осадок остался»…

    Reply
  12. Поручик

    (7) Сомневаюсь, что будет Новый РегулярноеВыражение в программе, предназначенной для построения учетных систем. Много случаев применения упоротого парсинга текста в типовых и нетиповых конфигурациях, обработках и часто такая потребность возникает? Не зря ведь встроенные регулярки имеются только в интерпретируемых языках, ориентированных именно на обработку текста.

    Reply
  13. SeiOkami

    (12) Поручик, не знаю, не знаю. Мне часто приходится работать с текстом средствами 1с. Уж намного чаще, чем ГенераторомСлучайныхЧисел.

    Reply
  14. V_V_V

    (4) ShantinTD, так вроде под Линукс еще нет версии компоненты. Как сказано в тексте статьи: «Основные планы на ближайшее будещее — сделать linux-версию.»

    (8) 1cge, «реализовал некий неполный аналог в виде функции» — что именно не проверяется, раз неполный?

    Reply
  15. lustin

    Друзья, вот я все понимаю, кроме одной вещи — ну почему нельзя почитать документацию и подумать как сделать по нормальному.

    Попробуйте отталкиваться от сценария использования:

    1. в системе вводится почтовый адрес

    2. адрес введенный пользователем проверяется на корректность.

    Таким образом возникает вопрос — какой адрес считать валидыным ? ответ — читаем стандарт http://tools.ietf.org/html/rfc2822#section-3.4.1

    Вопрос — Каким образом максимально быстро проверять адрес ? ответ — регулярными выражениями http://www.regular-expressions.info/email.html

    Вопрос — как в 1С использовать регулярные выражения ? Нативно в 1С можно использовать такие выражения с помощью библиотеки Александра Орефкова — https://snegopat.ru/scripts/doc/trunk/rex/readme.markdown сама библиотека уже внутри платформы.

    За сим откланиваюсь — глядишь ссылки кому то помогут сделать полноценный продукт-подсистему.

    Reply
  16. Vlad_M

    (14) Неполный аналог того что предложил автор статьи из приведенной ссылки.

    Reply
  17. ShantinTD
    Таким образом возникает вопрос — какой адрес считать валидыным ? ответ — читаем стандарт http://tools.ietf.org/html/rfc2822#section-3.4.1

    (15) lustin, спасибо. На один из вопросов (моих) ответил.

    Вопрос про то, кто разрешает (регистрирует) невалидные адреса — остается открытым.

    (12) Поручик, ничего вредного в появлении Новый РегулярноеВыражение не будет. Зато можно будет избежать «упоротых» разборов текста. В том числе — в типовых конфигурациях.

    (14) V_V_V, читаем несколькими строками выше — «всегда есть в 1С, даже под linux». Есть поправка — начиная с релиза 8.2.14. Но и это решается копированием файлов в нужную папку.

    Reply
  18. bpc222

    (1)

    +1.

    Представленный алгоритм «забанит» все адреса почтового сервера 1С 🙂

    Reply
  19. Vlad_M

    (18)(1) Спасибо. Внес изменение (чтобы работал домен 1С) 🙂

    Reply
  20. bpc222

    (19) 1cge,

    Вы не против, если мы будем использовать Ваш алгоритм в конфигурации онлайн сервиса http://infostart.ru/public/192077/ ?

    Написать свой — не вопрос, но раз уж есть готовое решение, почему бы и нет 🙂

    Reply
  21. Vlad_M

    (20)Не против

    Reply
  22. bpc222

    (21) 1cge,

    спасибо

    Reply
  23. Yashazz

    (12) Поручик, не чаще упоротых способов работы с xsd-схемами или ковариационного анализа. Любая строго формализованная учётная система, имеющая на постоянном неручном входе слабоформализованный поток мусора, обязана иметь средства разбора, в т.ч. регулярки. Это куда важней, чем дендрограммы или рандомайзеры.

    Reply
  24. kser87

    Эта функция из подсистемы «Базовая функциональность» из БСП 2.1. Модуль ОбщегоНазначенияКлиентСервер.

    Присутствует во всех конфигурациях такой подсистемой (Документооборот, БП, УПП и пр.)

    Reply
  25. Vlad_M

    (24) Эта самописная функция, источник указан.

    Действительно, в БСП появилась АдресЭлектроннойПочтыСоответствуетТребованиям(); 1) не знал :); 2) она немного другая, может в чем-то правильнее, в чем-то нет, например опять требует единственного вхождения «@» и соответственно по нему выделяет локальную и доменную часть.

    Reply
  26. ShantinTD

    (25) 1cge, поспорю. Читаем по приведенным в (15) lustin ссылкам, видим официальный стандарт на формат адреса электронной почты, и даже регулярное выражение, под которое должен попадать адрес. И адрес с двумя @ под него не попадает. Так что очень сомнительно, насколько «правильный» такой адрес. Хотя не отрицаю, что может существовать (наверное, сам не проверял и не встречал).

    Reply
  27. Vlad_M

    (26)http://tools.ietf.org/html/rfc2822#section-3.2.5.

    abc@def@example.com и «Abc@def»@example.com — правильные, потому что «@» экранировано «» или внутри закавыченной строки.

    Т.е. RFC такие адреса допускает, другое дело в реале маловероятно их использование, потому что почтовики не дадут создать такой адрес.

    Reply
  28. ShantinTD

    (27) 1cge, для проверки пользуюсь notepad++ и плагином RegEx Helper. Под вот то большое регулярное выражение попадает только второй адрес — с кавычками. Первый адрес — только после первой собаки начиная (def@example.com). Опять же: не утверждаю, что это истина в последней инстанции, но хотя бы есть с чем сравнить.

    Для справки: в обсуждениях вышеупомянутого мной RexV8 говорится о том, что VBScript и ICU работают по разному.

    Reply
  29. CagoBHuK

    (11) Мыши плакали и кололись, но продолжали жрать кактус.

    (0) Учите регулярки, учите внешние компоненты. На boost внешняя компонента для работы с regex пишется за полчаса под все платформы.

    Reply
  30. Vlad_M

    (29) Спасибо за совет…

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

    Плохо это или хорошо — другое дело и смотря с какой точки зрения 🙂

    Reply
  31. andrewks

    давным-давно писал подобное на 7.7. не думал, что кому-то ещё может быть интересно.

    Функция ПроверитьАдресЭлПочты(АдресЭлПочты)
    // проверка по RFC 5322 Section 3.2.3 (ограниченная)
    // ссылки:
    // http://tools.ietf.org/html/rfc5322
    // http://en.wikipedia.org/wiki/E-mail_address
    
    Адрес=СокрЛП(АдресЭлПочты);
    
    Если ПустоеЗначение(Адрес)=1 Тогда
    Возврат 0;
    КонецЕсли;
    
    Адрес1=Адрес;
    Поз=Найти(Адрес,»@»);
    Если Поз=0 Тогда
    Возврат 0;
    КонецЕсли;
    Пока Поз>0 Цикл
    Поз1=Поз;
    Адрес1=Сред(Адрес1,Поз+1);
    Поз=Найти(Адрес1,»@»);
    КонецЦикла;
    
    ДоменнаяЧасть=Адрес1;
    ЛокальнаяЧасть=Лев(Адрес,СтрДлина(Адрес)-СтрДлина(ДоменнаяЧасть)-1);
    
    Если (ПустоеЗначение(ДоменнаяЧасть)=1) ИЛИ (ПустоеЗначение(ЛокальнаяЧасть)=1) Тогда
    Возврат 0;
    КонецЕсли;
    
    Если (СтрДлина(ЛокальнаяЧасть)>64) ИЛИ (СтрДлина(ДоменнаяЧасть)>253) ИЛИ (СтрДлина(Адрес)>254) Тогда
    // превышена допустимая длина
    Возврат 0;
    КонецЕсли;
    
    // проверка локальной части
    
    Разрешенные=»ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstu­vwxyz0123456789!#$%&’*+-/=?#k8SjZc9Dxk_`{|}~.»;
    
    Если (Лев(ЛокальнаяЧасть,1)=».») ИЛИ (Прав(ЛокальнаяЧасть,1)=».») Тогда
    // точка в начале и в конце запрещена
    Возврат 0;
    КонецЕсли;
    
    Поз=Найти(ЛокальнаяЧасть,»..»);
    Если Поз>0 Тогда
    // удвоенная точка запрещена
    Возврат 0;
    КонецЕсли;
    
    Для нс=1 По СтрДлина(ЛокальнаяЧасть) Цикл
    Поз=Найти(Разрешенные,Сред(ЛокальнаяЧасть,нс,1));
    Если Поз=0 Тогда
    // встретился недопустимый символ
    Возврат 0;
    КонецЕсли;
    КонецЦикла;
    
    // проверка доменной части
    
    Разрешенные=»ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstu­vwxyz0123456789-.»;
    
    Если (Лев(ДоменнаяЧасть,1)=».») ИЛИ (Прав(ДоменнаяЧасть,1)=».») Тогда
    // точка в начале и в конце запрещена
    Возврат 0;
    КонецЕсли;
    
    Поз=Найти(ДоменнаяЧасть,»..»);
    Если Поз>0 Тогда
    // удвоенная точка запрещена
    Возврат 0;
    КонецЕсли;
    
    Для нс=1 По СтрДлина(ДоменнаяЧасть) Цикл
    Поз=Найти(Разрешенные,Сред(ДоменнаяЧасть,нс,1));
    Если Поз=0 Тогда
    // встретился недопустимый символ
    Возврат 0;
    КонецЕсли;
    КонецЦикла;
    
    // проверки завершены
    
    Возврат 1;
    КонецФункции
    
    

    Показать

    с пол-пинка переделывается под 8-ку

    Reply
  32. cargobird

    Если в доменном имени нет точки, или если в доменном имени после точки ничего не стоит — пишет, что адрес верен… Допилю, но вот.

    Reply
  33. cargobird

    Добавил еще:

     // 9. Доп.проверки
    ПозицияТочки = Найти(ДоменнаяЧасть, «.»);
    Если ПозицияТочки = 0 Тогда
    // нет точки
    Возврат ЛОЖЬ;
    ИначеЕсли ПозицияТочки = СтрДлина(ДоменнаяЧасть) Тогда
    // точка в конце и больше ничего нет
    Возврат ЛОЖЬ;
    КонецЕсли

    Показать

    Reply
  34. rossin

    Ошибка у вас.

    Пока Найти(УрезаемаяСтрока,»@») > 0 Цикл
    
    ИндексСобаки = ИндексСобаки + Найти(УрезаемаяСтрока,»@»);
    
    УрезаемаяСтрока = Сред(УрезаемаяСтрока, ИндексСобаки+1);
    
    КонецЦикла;
    

    Должно быть так:

    Пока Найти(УрезаемаяСтрока,»@») > 0 Цикл
    
    ИндексСобаки = ИндексСобаки + Найти(УрезаемаяСтрока,»@»);
    
    УрезаемаяСтрока = Сред(Адрес, ИндексСобаки+1);
    
    КонецЦикла;
    Reply
  35. andrey7617

    тоже добавил

    //++
    СимволыЭлПочты = «! # $ % & ‘ * + -/ = ? #k8SjZc9Dxk _ ` { | } ~ @»;
    //—
    ………………………
    
    ДлинаСтоки = СтрДлина(Адрес);
    Для Сч = 1 По ДлинаСтоки Цикл
    Если НЕ (Найти(ЛатинскиеБуквы,Сред(Адрес,Сч,1)) <> 0 ИЛИ Найти(Цифры,Сред(Адрес,Сч,1)) <> 0
    ИЛИ Найти(СимволыЭлПочты,Сред(Адрес,Сч,1)) <> 0 ) Тогда
    
    Возврат ЛОЖЬ;
    
    КонецЕсли;
    КонецЦикла;
    
    

    Показать

    Reply
  36. user942338

    У меня проходит такое «ee@eee@ee33» Ну а разве это адрес?

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *