gifts2017

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

Опубликовал Владислав Малец (Vlad_M) в раздел Программирование - Практика программирования

Описано решение проверки формата адреса 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 Тогда

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

        КонецЕсли;

    КонецЦикла;



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

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

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

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Александр (aet) 28.06.13 12:54
ЛокальнаяЧасть не проверяется на Не ЛатинскиеБуквы, а вот ДоменнаяЧасть, по-моему, может содержать Не ЛатинскиеБуквы.
2. Тимофей Шантин (ShantinTD) 28.06.13 13:25
А скорость работы?
Чем не устроили регулярные выражения?
3. Сергей Ожерельев (Поручик) 28.06.13 13:32
(2) Очевидно тем, что регулярки чуждые элементы в организме 1С Предприятия. Ну и работать в 1С они могут только в windows среде.
4. Тимофей Шантин (ShantinTD) 28.06.13 13:46
(3) Поручик, возражаю. Берем RexV8 и пользуемся почти что родной библиотекой (отвечает в 1С за Юникод) на любой системе (виндоус или линукс).
5. 1cge (Vlad_M) 28.06.13 13:59
(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 :)
SeiOkami; +1 Ответить
6. Александр Чернышов (HEKPOH) 28.06.13 14:26
1. По скорости описанный механизм проигрывает в 2 раза регулярам
2. "...регулярки чуждые элементы в организме 1С Предприятия..." Наверно, такие же чуждые, как и внешние компоненты)))
7. Яков Коган (Yashazz) 28.06.13 15:20
Насчёт чуждых - это хорошая отмазка пользоваться кривыми медленными велосипедами, вместо чтобы сделать по-человечески, да ещё наработанные ранее паттерны использовать...
Очень ждём родной объект - "Новый РегулярноеВыражение".
8. 1cge (Vlad_M) 28.06.13 15:30
(6)По поводу скорости - внутри отправки электронного письма исходящего из УТ11, даже если такая проверка в 2 раза медленнее регулярки - на время выполнения всей функции отправки влияния не зафиксировано. Если процесс длится 100 единиц, в него добавляют 2 варианта проверки - быстрый за 1 единицу и медленный за 2 - то это не критерий.

(7) по поводу наработанных паттернов - вновь про примеры корректных адресов, которые ими отвергаются Abc\@def@example.com, customer/department=shipping@example.com и !def!xyz%abc@example.com
9. Тимофей Шантин (ShantinTD) 28.06.13 17:14
1. Интересно посмотреть на "стандарт" относительно адресов электронной почты.
2. Интересно узнать: какой процент поставщиков услуг электронной почты (назовем их так) позволяет использовать исключительно корректные адреса?
К размышлению: в HTML5 определены поля ввода в том числе с типом email. То есть не просто текст, а именно адрес электронной почты. И есть проверка на правильность (валидность). Так вот каждый браузер сам реализует поддержку стандарта HTML5, и у каждого браузера получается разный результат при проверке одного и того же адреса. Причем так: некорректный адрес некоторыми браузерами определяется как корректный, и наоборот. (Сейчас уже не вспомню конкретно по браузерам, но в 2011 году проходил обучение - тестировал во всевозможных комбинациях).

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

А говорить, что "и так сойдет, все равно медленно работает" - неправильно. Просто в некоторых масштабах позволительно (?) не замечать потерь производительности.
10. Виталий Черненко (SeiOkami) 28.06.13 18:48
Лучше чуть медленнее, но родными средствами!
Так что спасибо =)
11. Тимофей Шантин (ShantinTD) 28.06.13 21:09
(10) SeiOkami, в "два раза" это не "чуть медленнее"...
Пример: кто-то втиснул в самописную конфу алгоритм расчета МД5. Родными средствами. То есть совсем родными - на встроенном языке. Ничего, что в 100 раз (!!! не шутка, без преувеличения) медленнее, чем внешними средствами. На 8.3 поправил, конечно, но "осадок остался"...
12. Сергей Ожерельев (Поручик) 28.06.13 21:21
(7) Сомневаюсь, что будет Новый РегулярноеВыражение в программе, предназначенной для построения учетных систем. Много случаев применения упоротого парсинга текста в типовых и нетиповых конфигурациях, обработках и часто такая потребность возникает? Не зря ведь встроенные регулярки имеются только в интерпретируемых языках, ориентированных именно на обработку текста.
13. Виталий Черненко (SeiOkami) 28.06.13 21:41
(12) Поручик, не знаю, не знаю. Мне часто приходится работать с текстом средствами 1с. Уж намного чаще, чем ГенераторомСлучайныхЧисел.
14. VVV (V_V_V) 29.06.13 15:27
(4) ShantinTD, так вроде под Линукс еще нет версии компоненты. Как сказано в тексте статьи: "Основные планы на ближайшее будещее - сделать linux-версию."
(8) 1cge, "реализовал некий неполный аналог в виде функции" - что именно не проверяется, раз неполный?
15. Алексей Лустин (lustin) 29.06.13 15:59
Друзья, вот я все понимаю, кроме одной вещи - ну почему нельзя почитать документацию и подумать как сделать по нормальному.

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

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 сама библиотека уже внутри платформы.

За сим откланиваюсь - глядишь ссылки кому то помогут сделать полноценный продукт-подсистему.
16. 1cge (Vlad_M) 29.06.13 20:06
(14) Неполный аналог того что предложил автор статьи из приведенной ссылки.
17. Тимофей Шантин (ShantinTD) 30.06.13 16:24
Таким образом возникает вопрос - какой адрес считать валидыным ? ответ - читаем стандарт http://tools.ietf.org/html/rfc2822#section-3.4.1

(15) lustin, спасибо. На один из вопросов (моих) ответил.
Вопрос про то, кто разрешает (регистрирует) невалидные адреса - остается открытым.
(12) Поручик, ничего вредного в появлении Новый РегулярноеВыражение не будет. Зато можно будет избежать "упоротых" разборов текста. В том числе - в типовых конфигурациях.
(14) V_V_V, читаем несколькими строками выше - "всегда есть в 1С, даже под linux". Есть поправка - начиная с релиза 8.2.14. Но и это решается копированием файлов в нужную папку.
18. Евгений Моисеенко (bpc222) 30.06.13 20:04
(1) aet,

+1.

Представленный алгоритм "забанит" все адреса почтового сервера 1С :)
19. 1cge (Vlad_M) 01.07.13 09:33
(18)(1) Спасибо. Внес изменение (чтобы работал домен 1С) :)
20. Евгений Моисеенко (bpc222) 01.07.13 10:15
(19) 1cge,

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

Написать свой - не вопрос, но раз уж есть готовое решение, почему бы и нет :)
21. 1cge (Vlad_M) 01.07.13 10:49
22. Евгений Моисеенко (bpc222) 01.07.13 11:04
23. Яков Коган (Yashazz) 01.07.13 11:45
(12) Поручик, не чаще упоротых способов работы с xsd-схемами или ковариационного анализа. Любая строго формализованная учётная система, имеющая на постоянном неручном входе слабоформализованный поток мусора, обязана иметь средства разбора, в т.ч. регулярки. Это куда важней, чем дендрограммы или рандомайзеры.
ShantinTD; +1 Ответить
24. Андрей Волин (kser87) 01.07.13 18:10
Эта функция из подсистемы "Базовая функциональность" из БСП 2.1. Модуль ОбщегоНазначенияКлиентСервер.

Присутствует во всех конфигурациях такой подсистемой (Документооборот, БП, УПП и пр.)
25. 1cge (Vlad_M) 02.07.13 05:42
(24) Эта самописная функция, источник указан.
Действительно, в БСП появилась АдресЭлектроннойПочтыСоответствуетТребованиям(); 1) не знал :); 2) она немного другая, может в чем-то правильнее, в чем-то нет, например опять требует единственного вхождения "@" и соответственно по нему выделяет локальную и доменную часть.
26. Тимофей Шантин (ShantinTD) 02.07.13 09:17
(25) 1cge, поспорю. Читаем по приведенным в (15) lustin ссылкам, видим официальный стандарт на формат адреса электронной почты, и даже регулярное выражение, под которое должен попадать адрес. И адрес с двумя @ под него не попадает. Так что очень сомнительно, насколько "правильный" такой адрес. Хотя не отрицаю, что может существовать (наверное, сам не проверял и не встречал).
27. 1cge (Vlad_M) 02.07.13 10:53
(26)http://tools.ietf.org/html/rfc2822#section-3.2.5.
abc\@def@example.com и "Abc@def"@example.com - правильные, потому что "@" экранировано "\" или внутри закавыченной строки.
Т.е. RFC такие адреса допускает, другое дело в реале маловероятно их использование, потому что почтовики не дадут создать такой адрес.
28. Тимофей Шантин (ShantinTD) 02.07.13 16:41
(27) 1cge, для проверки пользуюсь notepad++ и плагином RegEx Helper. Под вот то большое регулярное выражение попадает только второй адрес - с кавычками. Первый адрес - только после первой собаки начиная (def@example.com). Опять же: не утверждаю, что это истина в последней инстанции, но хотя бы есть с чем сравнить.
Для справки: в обсуждениях вышеупомянутого мной RexV8 говорится о том, что VBScript и ICU работают по разному.
29. Данила Елистратов (CagoBHuK) 04.07.13 11:33
(11) Мыши плакали и кололись, но продолжали жрать кактус.
(0) Учите регулярки, учите внешние компоненты. На boost внешняя компонента для работы с regex пишется за полчаса под все платформы.
30. Владислав Малец (Vlad_M) 04.07.13 11:46
(29) Спасибо за совет...
Там правда в тексте и описании было написано что проверка без использования внешних скриптов и регулярок, это так сказать одно из требований было. Так что за то и другое в курсе, не переживайте.
Плохо это или хорошо - другое дело и смотря с какой точки зрения :)
31. andrewks 04.07.13 18:56
давным-давно писал подобное на 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!#$%&'*+-/=?^_`{|}~.";
	
	Если (Лев(ЛокальнаяЧасть,1)=".") ИЛИ (Прав(ЛокальнаяЧасть,1)=".") Тогда
		// точка в начале и в конце запрещена
		Возврат 0;
	КонецЕсли;
	
	Поз=Найти(ЛокальнаяЧасть,"..");
	Если Поз>0 Тогда
		// удвоенная точка запрещена
		Возврат 0;
	КонецЕсли;
	
	Для нс=1 По СтрДлина(ЛокальнаяЧасть) Цикл
		Поз=Найти(Разрешенные,Сред(ЛокальнаяЧасть,нс,1));
		Если Поз=0 Тогда
			// встретился недопустимый символ
			Возврат 0;
		КонецЕсли;
	КонецЦикла;
	
	// проверка доменной части
	
	Разрешенные="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstu­vwxyz0123456789-.";
	
	Если (Лев(ДоменнаяЧасть,1)=".") ИЛИ (Прав(ДоменнаяЧасть,1)=".") Тогда
		// точка в начале и в конце запрещена
		Возврат 0;
	КонецЕсли;
	
	Поз=Найти(ДоменнаяЧасть,"..");
	Если Поз>0 Тогда
		// удвоенная точка запрещена
		Возврат 0;
	КонецЕсли;
	
	Для нс=1 По СтрДлина(ДоменнаяЧасть) Цикл
		Поз=Найти(Разрешенные,Сред(ДоменнаяЧасть,нс,1));
		Если Поз=0 Тогда
			// встретился недопустимый символ
			Возврат 0;
		КонецЕсли;
	КонецЦикла;
	
	// проверки завершены
	
	Возврат 1;
КонецФункции

...Показать Скрыть


с пол-пинка переделывается под 8-ку
32. Даниил Матвеев (cargobird) 20.08.15 08:39
Если в доменном имени нет точки, или если в доменном имени после точки ничего не стоит - пишет, что адрес верен... Допилю, но вот.
33. Даниил Матвеев (cargobird) 20.08.15 08:45
Добавил еще:
	// 9. Доп.проверки
	ПозицияТочки = Найти(ДоменнаяЧасть, ".");
	Если ПозицияТочки = 0 Тогда
		// нет точки
		Возврат ЛОЖЬ;
	ИначеЕсли ПозицияТочки = СтрДлина(ДоменнаяЧасть) Тогда
		// точка в конце и больше ничего нет
		Возврат ЛОЖЬ;
	КонецЕсли
...Показать Скрыть
maksa2005; +1 Ответить
34. Сергей Чернов (rossin) 19.02.16 18:54
Ошибка у вас.

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

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

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

    КонецЦикла;
...Показать Скрыть

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

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

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

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

    КонецЦикла;
...Показать Скрыть
35. Андрей Сергеев (andrey7617) 24.04.16 16:03
тоже добавил
//++
СимволыЭлПочты = "! # $ % & ' * + -/ = ? ^ _ ` { | } ~ @";
//--
...........................

ДлинаСтоки = СтрДлина(Адрес);
	Для Сч = 1 По ДлинаСтоки Цикл
		Если НЕ (Найти(ЛатинскиеБуквы,Сред(Адрес,Сч,1)) <> 0 ИЛИ Найти(Цифры,Сред(Адрес,Сч,1)) <> 0 
			  ИЛИ Найти(СимволыЭлПочты,Сред(Адрес,Сч,1)) <> 0 ) Тогда
		
		Возврат ЛОЖЬ;	
		
		КонецЕсли;
	КонецЦикла;

...Показать Скрыть
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа