Как проверить адрес электронной почты с помощью C++ с помощью CAtlRegExp

Мне нужно иметь возможность проверять различные форматы международных адресов электронной почты на С++. Я нашел много ответов в Интернете, не сокращайте это, и я нашел решение, которое хорошо работает для меня, и я подумал, что поделюсь со всеми, кто использует Библиотека сервера ATL

Какой-то фон. Я начал с этого сообщения: Использование регулярного выражения для проверки электронной почты адрес. Что указывало на http://emailregex.com/ с регулярным выражением на разных языках, поддерживающим Официальный стандарт RFC 5322 формата обмена сообщениями в Интернете.

Предоставленное регулярное выражение

(?:[a-z0-9!#$%&'+/=?^_`{|}~-]+(?:.[a-z0-9!#$%&'+/=?^_`{|}~-]+)|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d- \x7f]|\[\x01-\x09\x0b\x0c\x0e-\x7f])")@(?:(?:a-z0-9?.)+a-z0-9? |[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(? :25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0- 9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\[\x01-\x09\x0b\x0c\x0e-\x7f] )+)])

Я использую C++ с ATL Server Library, которая когда-то использовалась для быть частью Visual Studio. С тех пор Microsoft разместила его на CodePlex как с открытым исходным кодом. Мы все еще используем его для некоторых библиотек шаблонов. Моя цель — изменить это регулярное выражение, чтобы оно работало с CAtlRegEx


person tdemay    schedule 05.05.2017    source источник
comment
Подумайте, следует ли вам: regular-expressions.info/email.html -- какие вы используете адреса электронной почты для? Какие форматы требуются для кода, который их использует (отображает, отправляет электронные письма и т. д.)? Потому что действительный адрес электронной почты, который ваш движок не может использовать, является ложным срабатыванием. И правильно сформированный адрес электронной почты, которого не существует, является еще одним ложным срабатыванием.   -  person Yakk - Adam Nevraumont    schedule 05.05.2017
comment
Я думаю, вы предлагаете нам вообще пересмотреть проверку ввода. Это отличный момент. Как я только что подтвердил, Microsoft Outlook даже не проверяет недействительный адрес электронной почты. Он с радостью принял [email protected], хотя я быстро получил ответ по электронной почте, в котором говорилось, что адрес электронной почты имеет неверный формат. Мне нравится твоя мысль. В нашем продукте кто-то устанавливает правила, которые лицензируют контент для пользователей на основе их адресов электронной почты Active Directory. Наши клиенты настаивают на том, чтобы мы проверяли правило во время создания, чтобы пользователи, использующие правило позже, не получали ошибок.   -  person tdemay    schedule 05.05.2017
comment
Я не согласен с предположением авторов о том, что это регулярное выражение, которое он предоставляет, покрывает 99% адресов электронной почты, используемых сегодня. Может быть, в Соединенных Штатах это правда. Но это определенно не так глобально, так как я получаю свои &^#@! вручили мне в Китае в данный момент. Чем и вызван этот пост.   -  person tdemay    schedule 05.05.2017


Ответы (1)


Механизм регулярных выражений (CAtlRegExp) в ATL довольно прост. Мне удалось изменить регулярное выражение следующим образом:

^{([a-z0-9!#$%&'+/=?^_`{|}~\-]+(\.([a-z0-9!#$%&'< /em>+/=?^_`{|}~\-]+))*)@(((a-z0-9?\.)+a-z0-9?)|(\[((( 2((5[0-5])|([0-4][0-9])))|(1[0-9][0-9])|([1-9]?[0- 9]))\.)(((2((5[0-5])|([0-4][0-9])))|(1[0-9][0-9])| ([1-9]?[0-9]))\.)(((2((5[0-5])|([0-4][0-9])))|(1[0 -9][0-9])|([1-9]?[0-9]))\.)((2((5[0-5])|([0-4][0-9 ])))|(1[0-9][0-9])|([1-9]?[0-9]))\]))}$

Единственное, что кажется потерянным, — это поддержка Unicode в доменных именах, которую я смог решить, следуя примеру C# в Как: проверить, что строки имеют допустимый формат электронной почты в статье на MSDN, написанной используя Иднтоаскии.

В этом подходе имя пользователя и доменное имя извлекаются из адреса электронной почты. Доменное имя преобразуется в ASCII с помощью IdnToAscii, а затем они снова соединяются вместе, а затем прогоняются через регулярное выражение.

Имейте в виду, что обработка ошибок была опущена для удобочитаемости. Код необходим, чтобы убедиться, что нет переполнения буфера и другой обработки ошибок. Если кто-то передаст адрес электронной почты длиной более 255 символов, это приведет к сбою этого примера.

Код:

bool WINAPI LocalLooksLikeEmailAddress(LPCWSTR lpszEmailAddress) 
{
    bool bRetVal = true ;
    const int ccbEmailAddressMaxLen = 255 ;
    wchar_t achANSIEmailAddress[ccbEmailAddressMaxLen] = { L'\0' } ;
    ATL::CAtlRegExp<> regexp ;
    ATL::CAtlREMatchContext<> regexpMatch ;
    ATL::REParseError status  = regexp.Parse(L"^{.+}@{.+}$", FALSE) ;
    if (status == REPARSE_ERROR_OK) {
        if (regexp.Match(lpszEmailAddress, &regexpMatch) && regexpMatch.m_uNumGroups == 2) {
            const CAtlREMatchContext<>::RECHAR* szStart = 0 ;
            const CAtlREMatchContext<>::RECHAR* szEnd   = 0 ;
            regexpMatch.GetMatch(0, &szStart, &szEnd) ;
            ::wcsncpy_s(achANSIEmailAddress, szStart, (size_t)(szEnd - szStart)) ;
            regexpMatch.GetMatch(1, &szStart, &szEnd) ;
            wchar_t achDomainName[ccbEmailAddressMaxLen] = { L'\0' } ;
            ::wcsncpy_s(achDomainName, szStart, (size_t)(szEnd - szStart)) ;

            if (bRetVal) {
                wchar_t achPunycode[ccbEmailAddressMaxLen] = { L'\0' } ;
                if (IdnToAscii(0, achDomainName, -1, achPunycode, ccbEmailAddressMaxLen) == 0)
                    bRetVal = false ;
                else {
                    ::wcscat_s(achANSIEmailAddress, L"@") ;
                    ::wcscat_s(achANSIEmailAddress, achPunycode) ;
                }
            }
        }
    } 

    if (bRetVal) {
        status = regexp.Parse(
            L"^{([a-z0-9!#$%&'*+/=?^_`{|}~\\-]+(\\.([a-z0-9!#$%&'*+/=?^_`{|}~\\-]+))*)@((([a-z0-9]([a-z0-9\\-]*[a-z0-9])?\\.)+[a-z0-9]([a-z0-9\\-]*[a-z0-9])?)|(\\[(((2((5[0-5])|([0-4][0-9])))|(1[0-9][0-9])|([1-9]?[0-9]))\\.)(((2((5[0-5])|([0-4][0-9])))|(1[0-9][0-9])|([1-9]?[0-9]))\\.)(((2((5[0-5])|([0-4][0-9])))|(1[0-9][0-9])|([1-9]?[0-9]))\\.)((2((5[0-5])|([0-4][0-9])))|(1[0-9][0-9])|([1-9]?[0-9]))\\]))}$"
            , FALSE) ;
        if (status == REPARSE_ERROR_OK) {
            bRetVal = regexp.Match(achANSIEmailAddress, &regexpMatch) != 0;
        } 
    }

    return bRetVal ;
}

Стоит отметить, что этот подход не согласуется с результатами в C# Статья MSDN для двух адресов электронной почты. Глядя на исходное регулярное выражение, указанное на http://emailregex.com, можно предположить, что Статья MSDN ошибся, если спецификация не была недавно изменена. Я решил использовать регулярное выражение, упомянутое на http://emailregex.com.

Вот мои модульные тесты с использованием тех же адресов электронной почты из заголовка Статья MSDN

#include <Windows.h>
#if _DEBUG
#define TESTEXPR(expr) _ASSERTE(expr)
#else
#define TESTEXPR(expr) if (!(expr)) throw ;
#endif

void main()
{
    LPCWSTR validEmailAddresses[] = {   L"[email protected]", 
                                        L"[email protected]",
                                        L"[email protected]", 
                                        L"[email protected]", 
                                        L"js#[email protected]",
                                        L"j_9@[129.126.118.1]", 
                                        L"js*@proseware.com",            // <== according to https://msdn.microsoft.com/en-us/library/01escwtf(v=vs.110).aspx this is invalid
                                                                         // but according to http://emailregex.com/ that claims to support the RFC 5322 Official standard it's not. 
                                                                         // I'm going with valid
                                        L"[email protected]", 
                                        L"[email protected]",
                                        L"js@contoso.中国", 
                                        NULL } ;

    LPCWSTR invalidEmailAddresses[] = { L"[email protected]",
                                        L"\"j\\\"s\\\"\"@proseware.com", // <== according to https://msdn.microsoft.com/en-us/library/01escwtf(v=vs.110).aspx this is valid
                                                                         // but according to http://emailregex.com/ that claims to support the RFC 5322 Official standard it's not. 
                                                                         // I'm going with Invalid
                                        L"[email protected]",
                                        L"[email protected]",
                                        NULL } ;

    for (LPCWSTR* emailAddress = validEmailAddresses ; *emailAddress != NULL ; ++emailAddress)
    {
        TESTEXPR(LocalLooksLikeEmailAddress(*emailAddress)) ;
    }
    for (LPCWSTR* emailAddress = invalidEmailAddresses ; *emailAddress != NULL ; ++emailAddress)
    {
        TESTEXPR(!LocalLooksLikeEmailAddress(*emailAddress)) ;
    }
}
person tdemay    schedule 05.05.2017