Каков наилучший метод проверки адреса электронной почты Java?

Каковы хорошие библиотеки проверки адресов электронной почты для Java? Существуют ли альтернативы commons? валидатор?


person jon077    schedule 08.03.2009    source источник
comment
Я просто оставлю это здесь: davidcelis .com/блог/2012/09/06/   -  person mpenkov    schedule 30.10.2012
comment
Текущий URL для Commons: commons .apache.org/proper/commons-validator/apidocs/org/apache/   -  person james.garriss    schedule 16.06.2014
comment
Вы не должны использовать библиотеки (или регулярные выражения), которые не проходят всестороннюю проверку. Из-за сложности действительного адреса электронной почты не существует среднего уровня между отсутствием проверки и полной проверкой. Реализация Apache Commons неполна. Мне известна только одна библиотека (email-rfc2822-validator), но он по-прежнему работает с огромными регулярными выражениями. Комплексный лексер — это то, что вам действительно нужно. EmailValidator4J говорит, что работает, но у меня нет опыта работы с ним.   -  person Benny Bottema    schedule 29.12.2017
comment
@BennyBottema Вместо того, чтобы редактировать вопрос с комментариями, сделайте метапост, чтобы обсудить, почему он был закрыт, если у вас все еще есть вопросы.   -  person Machavity♦    schedule 29.06.2018


Ответы (18)


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

EDIT: была ошибка, где она была слишком ограничивает домен, из-за чего он не принимает действительные электронные письма от новых TLD.

Эта ошибка была устранена 03/янв/15 02:48 в commons-validator версии 1.4.1.

person Matthew Flaschen    schedule 08.03.2009
comment
Я согласен с приведенными вами дополнительными элементами, но являются ли они частью проекта Commons Validation? - person duffymo; 09.03.2009
comment
Нет, класс Apache EmailValidator не отправляет сообщение электронной почты для проверки. - person Matthew Flaschen; 14.07.2011
comment
Если ваш вариант использования заключается в проверке удаленного адреса электронной почты пользователя, это решение имеет существенный недостаток (похожий на InternetAddress.validate()): EmailValidator рассматривает user@[10.9.8.7] как действительные адреса электронной почты, которыми они являются в соответствии с RFC, но, возможно, не для регистрации пользователя/контактной формы. - person zillion1; 05.10.2011
comment
@zillion, задокументированный в Apache COMmons: эта реализация не гарантирует обнаружение всех возможных ошибок в адресе электронной почты. И я сказал, что вы должны сделать, чтобы убедиться, что это настоящее электронное письмо. Однако адреса с локальными IP-адресами могут быть действительными в редких случаях. - person Matthew Flaschen; 06.10.2011
comment
EmailValidator также дает вам возможность исключить локальные адреса электронной почты, чтобы должен был присутствовать домен верхнего уровня, что было для меня требованием. - person leojh; 30.01.2014
comment
EmailValidator очень, очень, очень прост. Он также ограничен ASCII. - person user1050755; 27.03.2014
comment
У Apache Commons EmailValidator есть один серьезный недостаток: он не поддерживает IDN. - person Piohen; 05.06.2014
comment
Метод isValid(emailString) библиотеки commons-validator-1.4.1.jar возвращает, что адрес электронной почты типа [email protected] недействителен, даже если это действительный адрес электронной почты. - person Bake; 22.05.2015
comment
commons-validator-1.4.1 сообщает Leo Notenboom <[email protected]> как недействительный, хотя он принимается обычными почтовыми серверами. - person fiffy; 29.05.2015
comment
Он также считает что-то вроде [email protected] действительным адресом электронной почты (попробовано с версией 1.4.1). Или он действительно действующий? - person Mikle Garin; 06.10.2015
comment
Проверьте commons.apache.org/proper/commons-validator/ для самая новая версия, и если она сломана, вы можете просто погуглить - person HendraWD; 07.06.2018

Проще всего использовать официальный почтовый пакет Java:

public static boolean isValidEmailAddress(String email) {
   boolean result = true;
   try {
      InternetAddress emailAddr = new InternetAddress(email);
      emailAddr.validate();
   } catch (AddressException ex) {
      result = false;
   }
   return result;
}
person Aaron Davidson    schedule 09.05.2011
comment
в самом простом, и вы, вероятно, уже имеете его в своем проекте для отправки электронных писем - person Eduardo; 02.09.2011
comment
Обратите внимание, что InternetAddress.validate() считает user@[10.9.8.7] и user@localhost действительными адресами электронной почты, которыми они являются в соответствии с RFC. Хотя, в зависимости от варианта использования (веб-форма), вы можете рассматривать их как недействительные. - person zillion1; 05.10.2011
comment
Кроме того, я считаю, что выбрасывать, а затем перехватывать исключения очень неэффективно. - person Nic Cottrell; 01.02.2012
comment
неэффективность не должна иметь значения, если вы не имеете дело со многими недействительными адресами электронной почты. - person TofuBeer; 09.05.2012
comment
не только это действительно, как сказал @zillion1, но и такие вещи, как bla@bla, считаются действительными. Действительно не лучшее решение. - person Diego Plentz; 20.11.2012
comment
@NicholasTolleyCottrell Это Java, здесь мы генерируем и перехватываем исключения, я не совсем понимаю вашу точку зрения - person gyorgyabraham; 25.01.2013
comment
Я хочу сказать, что создание исключений в Java является относительно дорогим процессом, тогда как выполнение проверки, возвращающей логическое значение, потребует гораздо меньших накладных расходов. - person Nic Cottrell; 25.01.2013
comment
Я подозреваю, что конструктор InternetAddress был подделан. Или мою систему взломали. Или RFC822 был подделан. Или мне действительно не помешало бы немного поспать прямо сейчас. Но я только что попробовал код, и все следующие пять строк проходят как действительные адреса электронной почты, если вы передаете их конструктору InternetAddress, и ясно, что они недействительны. Вот так: ., .com, com., abc и 123. Кроме того, добавление начальных или конечных пробелов также не делает строки недействительными. Ты будешь судьей! - person Martin Andersson; 20.03.2013
comment
Не будет ли это также возвращать true для John Doe <[email protected]>? Это действительный адрес для использования в API электронной почты, но он может быть недействительным в других случаях использования. - person Arjan; 05.04.2013
comment
Это не очень хороший ответ. Упомянутый валидатор слишком щедр на свои правила. Адреса электронной почты типа a@b не должны считаться действительными. - person Robert Kang; 27.11.2013
comment
Да, похоже, что этот метод считает asdf и 1234 действительными адресами электронной почты... - person Ajedi32; 01.12.2013
comment
Он даже проверяет один символ как действительный, что не может быть принято .. !! System.out.println(isValidEmailAddress("adafdad")); - person Sreedhar GS; 20.03.2014
comment
Правильно возвращает false для меня. - person Aaron Davidson; 20.03.2014
comment
@RobertKang это действительный адрес электронной почты в соответствии с RFC, даже если он не существует. - person jwenting; 06.08.2014
comment
это проходит нормально, но я не думаю, что так должно быть: new InternetAddress(";...'[email protected],").validate - person Erik Kaplun; 22.01.2015
comment
сыр не является действительным адресом электронной почты, но в соответствии с этим механизмом - person NimChimpsky; 09.02.2015
comment
гм, сыр не работает должным образом, когда я запускаю его. на какую, черт возьми, библиотеку javax.mail вы ссылаетесь??? - person Aaron Davidson; 12.02.2015
comment
Люди жалуются на все виды несуществующих, но действительных адресов электронной почты, которые проверяются. Это валидатор правильности адреса электронной почты, а не того, существует ли он! После валидатора веб-сайт всегда должен использовать механизм активации, чтобы проверить, существует ли адрес на самом деле. - person dstibbe; 20.03.2015
comment
он проверяет даже a//[email protected], чего, я полагаю, не должно - person brain storm; 11.06.2015
comment
a//[email protected] является синтаксически допустимым, и все валидаторы примут его (если только они не нарушены). - person David Balažic; 06.08.2015
comment
Почему InternetAddress не распознается в jdk8.51? - person Ismail Yavuz; 14.08.2015
comment
@ismailyavuz Вам нужно добавить библиотеку javax.mail. - person Majora320; 01.06.2016
comment
даже последняя версия 1.6.0 обрабатывает допустимые конечные точки: [email protected] - person j23; 06.12.2017

Валидатор Apache Commons можно использовать, как указано в других ответах.

пом.xml:

<dependency>
    <groupId>commons-validator</groupId>
    <artifactId>commons-validator</artifactId>
    <version>1.4.1</version>
</dependency>

построить.градле:

compile 'commons-validator:commons-validator:1.4.1'

Импорт:

import org.apache.commons.validator.routines.EmailValidator;

Код:

String email = "[email protected]";
boolean valid = EmailValidator.getInstance().isValid(email);

и разрешить локальные адреса

boolean allowLocal = true;
boolean valid = EmailValidator.getInstance(allowLocal).isValid(email);
person Aksel Willgert    schedule 01.11.2014
comment
Единственная проблема с apache commons заключается в том, что он позволяет использовать такие адреса электронной почты, как ab??$#@gmail.com. Хотя этот адрес может быть законным с точки зрения RFC, вероятно, это неверный ввод, введенный пользователем. - person Elad Tabak; 21.01.2015
comment
В Android Studio вы можете добавить compile 'commons-validator:commons-validator:1.4.1' в зависимости вашего приложения\build.gradle {} - person zOqvxf; 11.12.2015
comment
После того, как я попытался собрать свой проект, оказалось, что apache commons не очень хорошо работает с Android, сотни предупреждений и несколько ошибок, он даже не скомпилировался. Это то, что я закончил, используя howtodoinjava.com/2014/ 11/11/java-regex-validate-email-address - person zOqvxf; 12.12.2015
comment
У меня та же проблема, что и у Benjiko99. После добавления зависимости проект не скомпилируется, говорит, что java.exe завершился с ненулевым кодом выхода 2. - person Amit Mittal; 19.03.2016
comment
Я думаю, что лучший вариант - создать новый вопрос для отладки этого сбоя. - person Aksel Willgert; 19.03.2016
comment
Я также получал ошибки в Android Studio. Я перешел с 1.4.1 на 1.5.1 и все работает! - person Matt; 12.10.2016
comment
Примечание. Use_the Emailvalidator в org.apache.commons.validator.routines, поскольку EmailValidator в org.apache.commons.validator устарел (я использую 1.6 commons Validator) - person HopeKing; 22.12.2017
comment
Согласитесь, что Apache Commons лучше, чем официальный Java, который не помечал personname@gmail как недействительный. - person daticon; 19.01.2018
comment
Проверьте здесь новейшую версию commons.apache.org/proper/commons-validator/ или если ссылка не работает, вы можете просто поискать ее в Google. - person HendraWD; 07.06.2018

Поздний ответ, но я думаю, что это просто и достойно:

    public boolean isValidEmailAddress(String email) {
           String ePattern = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$";
           java.util.regex.Pattern p = java.util.regex.Pattern.compile(ePattern);
           java.util.regex.Matcher m = p.matcher(email);
           return m.matches();
    }

Тестовые случаи:

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

Для производственных целей проверки доменных имен должны выполняться по сети.

person Pujan    schedule 17.04.2013
comment
Это довольно упрощенный валидатор, который игнорирует большинство правил RFC вместе с IDN. Я бы избегал этого для любого приложения производственного качества. - person mlaccetti; 25.03.2014
comment
[email protected] будет недействительным... - person Alexander Burakevych; 20.06.2014
comment
Не создавайте свой собственный валидатор на основе регулярных выражений для вещей, охватываемых RFC. - person Josh Glover; 07.08.2014
comment
Изобретать велосипед — это нормально, если вы не возражаете против случайного прокола шины - person dldnh; 18.04.2015
comment
это хорошо, но не для всех случаев. - person Andrain; 02.02.2018

Если вы пытаетесь выполнить проверку формы, полученной от клиента, или просто проверку bean-компонента, сделайте это проще. Лучше проводить свободную проверку электронной почты, чем проводить строгую проверку и отклонять некоторых людей (например, когда они пытаются зарегистрироваться на вашем веб-сервисе). Почти все, что разрешено в части имени пользователя электронной почты, и так много новых доменов, которые добавляются буквально каждый месяц (например, .company, .entreprise, .estate), безопаснее не вводить ограничения:

Pattern pattern = Pattern.compile("^.+@.+\\..+$");
Matcher matcher = pattern.matcher(email);
person Alexander Burakevych    schedule 20.06.2014
comment
это действительно хороший момент, любое разумное приложение должно иметь другие меры, чтобы предотвратить использование этого ввода в будущем. - person jmaculate; 12.12.2014
comment
Как насчет того, чтобы изменить его на ^.+@.+(\\.[^\\.]+)+$, чтобы избежать точки в конце? - person Alpha Huang; 30.06.2015

Поздно с вопросом здесь, но: я веду класс по этому адресу: http://lacinato.com/cm/software/emailrelated/emailaddress

Он основан на классе Леса Хэзлвуда, но имеет множество улучшений и исправляет несколько ошибок. Лицензия Апача.

Я считаю, что это самый мощный парсер электронной почты на Java, и я еще не видел более способного на каком-либо языке, хотя такой может быть. Это не синтаксический анализатор в стиле лексера, но он использует сложное регулярное выражение Java и, следовательно, не так эффективен, как мог бы быть, но моя компания проанализировала с его помощью более 10 миллиардов реальных адресов: его, безусловно, можно использовать в высокопроизводительном ситуация. Может быть, раз в год он попадет на адрес, который вызывает переполнение стека регулярных выражений (соответственно), но это спам-адреса, которые состоят из сотен или тысяч символов с множеством кавычек, скобок и тому подобного.

RFC 2822 и связанные с ним спецификации действительно достаточно либеральны в отношении адресов электронной почты, поэтому класс как это излишество для большинства применений. Например, следующий адрес является легитимным, согласно спецификации, с пробелами и прочим:

"<bob \" (here) " < (hi there) "bob(the man)smith" (hi) @ (there) example.com (hello) > (again)

Ни один почтовый сервер не допустит этого, но этот класс может разобрать его (и переписать в удобную форму).

Мы обнаружили, что существующие параметры анализатора электронной почты Java недостаточно надежны (это означает, что все они не могут анализировать некоторые действительные адреса), поэтому мы создали этот класс.

Код хорошо документирован и имеет множество легко изменяемых параметров, позволяющих разрешить или запретить определенные формы электронной почты. Он также предоставляет множество методов для доступа к определенным частям адреса (левая сторона, правая сторона, личные имена, комментарии и т. д.), для анализа/проверки заголовков списка почтовых ящиков, для анализа/проверки обратного пути. (уникальный среди заголовков) и т.д.

Написанный код имеет зависимость от javamail, но его легко удалить, если вам не нужны второстепенные функции, которые он предоставляет.

person lacinato    schedule 30.10.2012
comment
Привет, я скопировал его в GitHub для всеобщего доступа к сообществу с открытым исходным кодом. Теперь каждый может комментировать, документировать и улучшать код. github.com/bbottema/email-rfc2822-validator. Раньше я использовал старую версию Леса, но мне пришлось удалить ее из-за ошибок зависания регулярных выражений: leshazlewood.com/2006/11/06/emailaddress-java-class/ - person Benny Bottema; 26.02.2016

Мне просто интересно, почему никто не придумал @Email из дополнительных ограничений Hibernate Validator. Сам валидатор — EmailValidator.

person Markus Malkusch    schedule 01.04.2014
comment
Несмотря на то, что это альтернатива Apache commons, его реализация столь же рудиментарна, как и большинство библиотек на основе регулярных выражений. Из документации: Однако, как обсуждается в этой статье, не всегда целесообразно внедрять на 100% совместимый валидатор электронной почты. Единственный известный мне всеобъемлющий валидатор на основе регулярных выражений — это email-rfc2822-validator, а в противном случае EmailValidator4J кажется многообещающим. - person Benny Bottema; 29.12.2017

Les Hazlewood написал очень подробный класс проверки электронной почты, совместимый с RFC 2822, с использованием регулярных выражений Java. Вы можете найти его по адресу http://www.leshazlewood.com/?p=23. . Однако его тщательность (или реализация Java RE) приводит к неэффективности — читайте комментарии о времени разбора длинных адресов.

person Philip    schedule 24.08.2009
comment
Я основывался на превосходном классе Леса Хэзлвуда (в котором есть некоторые ошибки). (См. мой отдельный ответ на этот вопрос.) Хотя я поддерживал метод регулярного выражения Java, мы прекрасно используем его в среде, критической по производительности. Если все, что вы делаете, это разбор адресов, производительность может быть проблемой, но я подозреваю, что для большинства пользователей это только начало того, что они делают. Мои обновления в классе также исправили ряд проблем с длинной рекурсией. - person lacinato; 30.10.2012
comment
Это устаревшая библиотека, которая дважды заменялась, наконец, email-rfc2822-validator. . Хотя он по-прежнему соответствует всем современным требованиям, он также по-прежнему подвержен скрытым ошибкам производительности (и не поддерживает ограниченные изменения, внесенные более новыми спецификациями RFC). - person Benny Bottema; 29.12.2017

Я перенес часть кода в Zend_Validator_Email:

@FacesValidator("emailValidator")
public class EmailAddressValidator implements Validator {

    private String localPart;
    private String hostName;
    private boolean domain = true;

    Locale locale;
    ResourceBundle bundle;

    private List<FacesMessage> messages = new ArrayList<FacesMessage>();

    private HostnameValidator hostnameValidator;

    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        setOptions(component);
        String email    = (String) value;
        boolean result  = true;
        Pattern pattern = Pattern.compile("^(.+)@([^@]+[^.])$");
        Matcher matcher = pattern.matcher(email);

        locale = context.getViewRoot().getLocale();
        bundle = ResourceBundle.getBundle("com.myapp.resources.validationMessages", locale);

        boolean length = true;
        boolean local  = true;

        if (matcher.find()) {
            localPart   = matcher.group(1);
            hostName    = matcher.group(2);

            if (localPart.length() > 64 || hostName.length() > 255) {
                length          = false;
                addMessage("enterValidEmail", "email.AddressLengthExceeded");
            } 

            if (domain == true) {
                hostnameValidator = new HostnameValidator();
                hostnameValidator.validate(context, component, hostName);
            }

            local = validateLocalPart();

            if (local && length) {
                result = true;
            } else {
                result = false;
            }

        } else {
            result          = false;
            addMessage("enterValidEmail", "invalidEmailAddress");
        }

        if (result == false) {
            throw new ValidatorException(messages);
        }

    }

    private boolean validateLocalPart() {
        // First try to match the local part on the common dot-atom format
        boolean result = false;

        // Dot-atom characters are: 1*atext *("." 1*atext)
        // atext: ALPHA / DIGIT / and "!", "#", "$", "%", "&", "'", "*",
        //        "+", "-", "/", "=", "?", "^", "_", "`", "{", "|", "}", "~"
        String atext = "a-zA-Z0-9\\u0021\\u0023\\u0024\\u0025\\u0026\\u0027\\u002a"
                + "\\u002b\\u002d\\u002f\\u003d\\u003f\\u005e\\u005f\\u0060\\u007b"
                + "\\u007c\\u007d\\u007e";
        Pattern regex = Pattern.compile("^["+atext+"]+(\\u002e+["+atext+"]+)*$");
        Matcher matcher = regex.matcher(localPart);
        if (matcher.find()) {
            result = true;
        } else {
            // Try quoted string format

            // Quoted-string characters are: DQUOTE *([FWS] qtext/quoted-pair) [FWS] DQUOTE
            // qtext: Non white space controls, and the rest of the US-ASCII characters not
            //   including "\" or the quote character
            String noWsCtl = "\\u0001-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f";
            String qText = noWsCtl + "\\u0021\\u0023-\\u005b\\u005d-\\u007e";
            String ws = "\\u0020\\u0009";

            regex = Pattern.compile("^\\u0022(["+ws+qText+"])*["+ws+"]?\\u0022$");
            matcher = regex.matcher(localPart);
            if (matcher.find()) {
                result = true;
            } else {
                addMessage("enterValidEmail", "email.AddressDotAtom");
                addMessage("enterValidEmail", "email.AddressQuotedString");
                addMessage("enterValidEmail", "email.AddressInvalidLocalPart");
            }
        }

        return result;
    }

    private void addMessage(String detail, String summary) {
        String detailMsg = bundle.getString(detail);
        String summaryMsg = bundle.getString(summary);
        messages.add(new FacesMessage(FacesMessage.SEVERITY_ERROR, summaryMsg, detailMsg));
    }

    private void setOptions(UIComponent component) {
        Boolean domainOption = Boolean.valueOf((String) component.getAttributes().get("domain"));
        //domain = (domainOption == null) ? true : domainOption.booleanValue();
    }
}

С валидатором имени хоста следующим образом:

@FacesValidator("hostNameValidator")
public class HostnameValidator implements Validator {

    private Locale locale;
    private ResourceBundle bundle;
    private List<FacesMessage> messages;
    private boolean checkTld = true;
    private boolean allowLocal = false;
    private boolean allowDNS = true;
    private String tld;
    private String[] validTlds = {"ac", "ad", "ae", "aero", "af", "ag", "ai",
        "al", "am", "an", "ao", "aq", "ar", "arpa", "as", "asia", "at", "au",
        "aw", "ax", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi", "biz",
        "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by", "bz", "ca",
        "cat", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co",
        "com", "coop", "cr", "cu", "cv", "cx", "cy", "cz", "de", "dj", "dk",
        "dm", "do", "dz", "ec", "edu", "ee", "eg", "er", "es", "et", "eu", "fi",
        "fj", "fk", "fm", "fo", "fr", "ga", "gb", "gd", "ge", "gf", "gg", "gh",
        "gi", "gl", "gm", "gn", "gov", "gp", "gq", "gr", "gs", "gt", "gu", "gw",
        "gy", "hk", "hm", "hn", "hr", "ht", "hu", "id", "ie", "il", "im", "in",
        "info", "int", "io", "iq", "ir", "is", "it", "je", "jm", "jo", "jobs",
        "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp", "kr", "kw", "ky", "kz",
        "la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma",
        "mc", "md", "me", "mg", "mh", "mil", "mk", "ml", "mm", "mn", "mo",
        "mobi", "mp", "mq", "mr", "ms", "mt", "mu", "museum", "mv", "mw", "mx",
        "my", "mz", "na", "name", "nc", "ne", "net", "nf", "ng", "ni", "nl",
        "no", "np", "nr", "nu", "nz", "om", "org", "pa", "pe", "pf", "pg", "ph",
        "pk", "pl", "pm", "pn", "pr", "pro", "ps", "pt", "pw", "py", "qa", "re",
        "ro", "rs", "ru", "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si",
        "sj", "sk", "sl", "sm", "sn", "so", "sr", "st", "su", "sv", "sy", "sz",
        "tc", "td", "tel", "tf", "tg", "th", "tj", "tk", "tl", "tm", "tn", "to",
        "tp", "tr", "travel", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
        "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu", "wf", "ws",
        "ye", "yt", "yu", "za", "zm", "zw"};
    private Map<String, Map<Integer, Integer>> idnLength;

    private void init() {
        Map<Integer, Integer> biz = new HashMap<Integer, Integer>();
        biz.put(5, 17);
        biz.put(11, 15);
        biz.put(12, 20);

        Map<Integer, Integer> cn = new HashMap<Integer, Integer>();
        cn.put(1, 20);

        Map<Integer, Integer> com = new HashMap<Integer, Integer>();
        com.put(3, 17);
        com.put(5, 20);

        Map<Integer, Integer> hk = new HashMap<Integer, Integer>();
        hk.put(1, 15);

        Map<Integer, Integer> info = new HashMap<Integer, Integer>();
        info.put(4, 17);

        Map<Integer, Integer> kr = new HashMap<Integer, Integer>();
        kr.put(1, 17);

        Map<Integer, Integer> net = new HashMap<Integer, Integer>();
        net.put(3, 17);
        net.put(5, 20);

        Map<Integer, Integer> org = new HashMap<Integer, Integer>();
        org.put(6, 17);

        Map<Integer, Integer> tw = new HashMap<Integer, Integer>();
        tw.put(1, 20);

        Map<Integer, Integer> idn1 = new HashMap<Integer, Integer>();
        idn1.put(1, 20);

        Map<Integer, Integer> idn2 = new HashMap<Integer, Integer>();
        idn2.put(1, 20);

        Map<Integer, Integer> idn3 = new HashMap<Integer, Integer>();
        idn3.put(1, 20);

        Map<Integer, Integer> idn4 = new HashMap<Integer, Integer>();
        idn4.put(1, 20);

        idnLength = new HashMap<String, Map<Integer, Integer>>();

        idnLength.put("BIZ", biz);
        idnLength.put("CN", cn);
        idnLength.put("COM", com);
        idnLength.put("HK", hk);
        idnLength.put("INFO", info);
        idnLength.put("KR", kr);
        idnLength.put("NET", net);
        idnLength.put("ORG", org);
        idnLength.put("TW", tw);
        idnLength.put("ایران", idn1);
        idnLength.put("中国", idn2);
        idnLength.put("公司", idn3);
        idnLength.put("网络", idn4);

        messages = new ArrayList<FacesMessage>();
    }

    public HostnameValidator() {
        init();
    }

    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        String hostName = (String) value;

        locale = context.getViewRoot().getLocale();
        bundle = ResourceBundle.getBundle("com.myapp.resources.validationMessages", locale);

        Pattern ipPattern = Pattern.compile("^[0-9a-f:\\.]*$", Pattern.CASE_INSENSITIVE);
        Matcher ipMatcher = ipPattern.matcher(hostName);
        if (ipMatcher.find()) {
            addMessage("hostname.IpAddressNotAllowed");
            throw new ValidatorException(messages);
        }

        boolean result = false;

        // removes last dot (.) from hostname 
        hostName = hostName.replaceAll("(\\.)+$", "");
        String[] domainParts = hostName.split("\\.");

        boolean status = false;

        // Check input against DNS hostname schema
        if ((domainParts.length > 1) && (hostName.length() > 4) && (hostName.length() < 255)) {
            status = false;

            dowhile:
            do {
                // First check TLD
                int lastIndex = domainParts.length - 1;
                String domainEnding = domainParts[lastIndex];
                Pattern tldRegex = Pattern.compile("([^.]{2,10})", Pattern.CASE_INSENSITIVE);
                Matcher tldMatcher = tldRegex.matcher(domainEnding);
                if (tldMatcher.find() || domainEnding.equals("ایران")
                        || domainEnding.equals("中国")
                        || domainEnding.equals("公司")
                        || domainEnding.equals("网络")) {



                    // Hostname characters are: *(label dot)(label dot label); max 254 chars
                    // label: id-prefix [*ldh{61} id-prefix]; max 63 chars
                    // id-prefix: alpha / digit
                    // ldh: alpha / digit / dash

                    // Match TLD against known list
                    tld = (String) tldMatcher.group(1).toLowerCase().trim();
                    if (checkTld == true) {
                        boolean foundTld = false;
                        for (int i = 0; i < validTlds.length; i++) {
                            if (tld.equals(validTlds[i])) {
                                foundTld = true;
                            }
                        }

                        if (foundTld == false) {
                            status = false;
                            addMessage("hostname.UnknownTld");
                            break dowhile;
                        }
                    }

                    /**
                     * Match against IDN hostnames
                     * Note: Keep label regex short to avoid issues with long patterns when matching IDN hostnames
                     */
                    List<String> regexChars = getIdnRegexChars();

                    // Check each hostname part
                    int check = 0;
                    for (String domainPart : domainParts) {
                        // Decode Punycode domainnames to IDN
                        if (domainPart.indexOf("xn--") == 0) {
                            domainPart = decodePunycode(domainPart.substring(4));
                        }

                        // Check dash (-) does not start, end or appear in 3rd and 4th positions
                        if (domainPart.indexOf("-") == 0
                                || (domainPart.length() > 2 && domainPart.indexOf("-", 2) == 2 && domainPart.indexOf("-", 3) == 3)
                                || (domainPart.indexOf("-") == (domainPart.length() - 1))) {
                            status = false;
                            addMessage("hostname.DashCharacter");
                            break dowhile;
                        }

                        // Check each domain part
                        boolean checked = false;

                        for (int key = 0; key < regexChars.size(); key++) {
                            String regexChar = regexChars.get(key);
                            Pattern regex = Pattern.compile(regexChar);
                            Matcher regexMatcher = regex.matcher(domainPart);
                            status = regexMatcher.find();
                            if (status) {
                                int length = 63;

                                if (idnLength.containsKey(tld.toUpperCase())
                                        && idnLength.get(tld.toUpperCase()).containsKey(key)) {
                                    length = idnLength.get(tld.toUpperCase()).get(key);
                                }

                                int utf8Length;
                                try {
                                    utf8Length = domainPart.getBytes("UTF8").length;
                                    if (utf8Length > length) {
                                        addMessage("hostname.InvalidHostname");
                                    } else {
                                        checked = true;
                                        break;
                                    }
                                } catch (UnsupportedEncodingException ex) {
                                    Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
                                }


                            }
                        }


                        if (checked) {
                            ++check;
                        }
                    }

                    // If one of the labels doesn't match, the hostname is invalid
                    if (check != domainParts.length) {
                        status = false;
                        addMessage("hostname.InvalidHostnameSchema");

                    }
                } else {
                    // Hostname not long enough
                    status = false;
                    addMessage("hostname.UndecipherableTld");
                }

            } while (false);

            if (status == true && allowDNS) {
                result = true;
            }

        } else if (allowDNS == true) {
            addMessage("hostname.InvalidHostname");
            throw new ValidatorException(messages);
        }

        // Check input against local network name schema;
        Pattern regexLocal = Pattern.compile("^(([a-zA-Z0-9\\x2d]{1,63}\\x2e)*[a-zA-Z0-9\\x2d]{1,63}){1,254}$", Pattern.CASE_INSENSITIVE);
        boolean checkLocal = regexLocal.matcher(hostName).find();
        if (allowLocal && !status) {
            if (checkLocal) {
                result = true;
            } else {
                // If the input does not pass as a local network name, add a message
                result = false;
                addMessage("hostname.InvalidLocalName");
            }
        }


        // If local network names are not allowed, add a message
        if (checkLocal && !allowLocal && !status) {
            result = false;
            addMessage("hostname.LocalNameNotAllowed");
        }

        if (result == false) {
            throw new ValidatorException(messages);
        }

    }

    private void addMessage(String msg) {
        String bundlMsg = bundle.getString(msg);
        messages.add(new FacesMessage(FacesMessage.SEVERITY_ERROR, bundlMsg, bundlMsg));
    }

    /**
     * Returns a list of regex patterns for the matched TLD
     * @param tld
     * @return 
     */
    private List<String> getIdnRegexChars() {
        List<String> regexChars = new ArrayList<String>();
        regexChars.add("^[a-z0-9\\x2d]{1,63}$");
        Document doc = null;
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);

        try {
            InputStream validIdns = getClass().getClassLoader().getResourceAsStream("com/myapp/resources/validIDNs_1.xml");
            DocumentBuilder builder = factory.newDocumentBuilder();
            doc = builder.parse(validIdns);
            doc.getDocumentElement().normalize();
        } catch (SAXException ex) {
            Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
        }

        // prepare XPath
        XPath xpath = XPathFactory.newInstance().newXPath();

        NodeList nodes = null;
        String xpathRoute = "//idn[tld=\'" + tld.toUpperCase() + "\']/pattern/text()";

        try {
            XPathExpression expr;
            expr = xpath.compile(xpathRoute);
            Object res = expr.evaluate(doc, XPathConstants.NODESET);
            nodes = (NodeList) res;
        } catch (XPathExpressionException ex) {
            Logger.getLogger(HostnameValidator.class.getName()).log(Level.SEVERE, null, ex);
        }


        for (int i = 0; i < nodes.getLength(); i++) {
            regexChars.add(nodes.item(i).getNodeValue());
        }

        return regexChars;
    }

    /**
     * Decode Punycode string
     * @param encoded
     * @return 
         */
    private String decodePunycode(String encoded) {
        Pattern regex = Pattern.compile("([^a-z0-9\\x2d]{1,10})", Pattern.CASE_INSENSITIVE);
        Matcher matcher = regex.matcher(encoded);
        boolean found = matcher.find();

        if (encoded.isEmpty() || found) {
            // no punycode encoded string, return as is
            addMessage("hostname.CannotDecodePunycode");
            throw new ValidatorException(messages);
        }

        int separator = encoded.lastIndexOf("-");
            List<Integer> decoded = new ArrayList<Integer>();
        if (separator > 0) {
            for (int x = 0; x < separator; ++x) {
                decoded.add((int) encoded.charAt(x));
            }
        } else {
            addMessage("hostname.CannotDecodePunycode");
            throw new ValidatorException(messages);
        }

        int lengthd = decoded.size();
        int lengthe = encoded.length();

        // decoding
        boolean init = true;
        int base = 72;
        int index = 0;
        int ch = 0x80;

        int indexeStart = (separator == 1) ? (separator + 1) : 0;
        for (int indexe = indexeStart; indexe < lengthe; ++lengthd) {
            int oldIndex = index;
            int pos = 1;
            for (int key = 36; true; key += 36) {
                int hex = (int) encoded.charAt(indexe++);
                int digit = (hex - 48 < 10) ? hex - 22
                        : ((hex - 65 < 26) ? hex - 65
                        : ((hex - 97 < 26) ? hex - 97
                        : 36));

                index += digit * pos;
                int tag = (key <= base) ? 1 : ((key >= base + 26) ? 26 : (key - base));
                if (digit < tag) {
                    break;
                }
                pos = (int) (pos * (36 - tag));
            }
            int delta = (int) (init ? ((index - oldIndex) / 700) : ((index - oldIndex) / 2));
            delta += (int) (delta / (lengthd + 1));
            int key;
            for (key = 0; delta > 910; key += 36) {
                delta = (int) (delta / 35);
            }
            base = (int) (key + 36 * delta / (delta + 38));
            init = false;
            ch += (int) (index / (lengthd + 1));
            index %= (lengthd + 1);
            if (lengthd > 0) {
                for (int i = lengthd; i > index; i--) {
                    decoded.set(i, decoded.get(i - 1));
                }
            }

            decoded.set(index++, ch);
        }

        // convert decoded ucs4 to utf8 string
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < decoded.size(); i++) {
            int value = decoded.get(i);
            if (value < 128) {
                sb.append((char) value);
            } else if (value < (1 << 11)) {
                sb.append((char) (192 + (value >> 6)));
                sb.append((char) (128 + (value & 63)));
            } else if (value < (1 << 16)) {
                sb.append((char) (224 + (value >> 12)));
                sb.append((char) (128 + ((value >> 6) & 63)));
                sb.append((char) (128 + (value & 63)));
            } else if (value < (1 << 21)) {
                sb.append((char) (240 + (value >> 18)));
                sb.append((char) (128 + ((value >> 12) & 63)));
                sb.append((char) (128 + ((value >> 6) & 63)));
                sb.append((char) (128 + (value & 63)));
            } else {
                addMessage("hostname.CannotDecodePunycode");
                throw new ValidatorException(messages);
            }
        }

        return sb.toString();

    }

    /**
     * Eliminates empty values from input array
     * @param data
     * @return 
     */
    private String[] verifyArray(String[] data) {
        List<String> result = new ArrayList<String>();
        for (String s : data) {
            if (!s.equals("")) {
                result.add(s);
            }
        }

        return result.toArray(new String[result.size()]);
    }
}

И файл validIDNs.xml с шаблонами регулярных выражений для разных tld (слишком большой, чтобы его включать :)

<idnlist>
    <idn>
        <tld>AC</tld>
        <pattern>^[\u002d0-9a-zà-öø-ÿāăąćĉċčďđēėęěĝġģĥħīįĵķĺļľŀłńņňŋőœŕŗřśŝşšţťŧūŭůűųŵŷźżž]{1,63}$</pattern>
    </idn>
    <idn>
        <tld>AR</tld>
        <pattern>^[\u002d0-9a-zà-ãç-êìíñ-õü]{1,63}$</pattern>
    </idn>
    <idn>
        <tld>AS</tld>
        <pattern>/^[\u002d0-9a-zà-öø-ÿāăąćĉċčďđēĕėęěĝğġģĥħĩīĭįıĵķĸĺļľłńņňŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷźż]{1,63}$</pattern>
    </idn>
    <idn>
        <tld>AT</tld>
        <pattern>/^[\u002d0-9a-zà-öø-ÿœšž]{1,63}$</pattern>
    </idn>
    <idn>
        <tld>BIZ</tld>
        <pattern>^[\u002d0-9a-zäåæéöøü]{1,63}$</pattern>
        <pattern>^[\u002d0-9a-záéíñóúü]{1,63}$</pattern>
        <pattern>^[\u002d0-9a-záéíóöúüőű]{1,63}$</pattern>
    </id>
</idlist>
person Erick Martinez    schedule 12.07.2011
comment
Этот ответ больше не применим по очевидным причинам. Удалите проверку TLD, и это, вероятно, приемлемо, если вы хотите принимать неанглийские адреса электронной почты. - person Christopher Schneider; 14.04.2016

Если вы хотите проверить, действителен ли адрес электронной почты, то VRFY получит вы каким-то образом. Я обнаружил, что это полезно для проверки адресов интрасеть (то есть адресов электронной почты для внутренних сайтов). Однако это менее полезно для почтовых серверов в Интернете (см. предостережения в верхней части этой страницы).

person Brian Agnew    schedule 09.03.2009

Хотя существует множество альтернатив Apache commons, их реализации в лучшем случае рудиментарны (например, реализация Apache commons сама по себе) и даже абсолютно ошибочны в других случаях.

Я бы также держался подальше от так называемого простого «неограничительного» регулярного выражения; нет такого понятия. Например, @ разрешено несколько раз в зависимости от контекста. а как узнать что нужный есть? Простое регулярное выражение не поймет этого, даже если электронная почта должна быть действительной. Все более сложное становится подвержен ошибкам или даже содержать скрытые убийцы производительности. Как вы собираетесь поддерживать что-то вроде this< /а>?

Единственный известный мне комплексный валидатор на основе регулярных выражений, совместимый с RFC, — это email-rfc2822-validator с его «усовершенствованным» регулярным выражением с соответствующим именем Dragons.java. Однако он поддерживает только старую спецификацию RFC-2822, хотя и достаточно подходящую для современных нужд (RFC- 5322 обновляет в областях, которые уже выходят за рамки для повседневного использования).

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

Другой вариант, который у вас есть, — это использование веб-сервиса, такого как проверенный в боевых условиях веб-сервис проверки или API Mailboxlayer (только что получил первые результаты Google). Он не строго совместим с RFC, но работает достаточно хорошо для современных нужд.

person Benny Bottema    schedule 29.12.2017

Что вы хотите проверить? Адрес электронной почты?

Адрес электронной почты можно проверить только на соответствие формату. См. стандарт: RFC2822. Лучший способ сделать это - регулярное выражение. Вы никогда не узнаете, существует ли он на самом деле, не отправив электронное письмо.

Я проверил общедоступный валидатор. Он содержит класс org.apache.commons.validator.EmailValidator. Кажется, это хорошая отправная точка.

person ReneS    schedule 09.03.2009
comment
Я не уверен, что регулярное выражение — лучший способ сделать это, оно совершенно нечитаемо, если вы собираетесь следовать RFC до письмо - person user2813274; 01.11.2014
comment
Согласитесь с @user2813274, вам нужен правильный лексер, а не регулярное выражение спагетти. - person Benny Bottema; 29.12.2017

Текущая версия Apache Commons Validator: 1.3.1.

Проверяющий класс — org.apache.commons.validator.EmailValidator. Есть импорт для org.apache.oro.text.perl. Perl5Util из удаленного проекта Jakarta ORO.

Кстати, я обнаружил, что существует версия 1.4, вот документация по API. На сайте указано: "Последняя публикация: 05 марта 2008 г. | Версия: 1.4-SNAPSHOT", но это не окончательно. Единственный способ собрать самому (но это снимок, а не РЕЛИЗ) и использовать, или скачать с здесь. Это означает, что версия 1.4 не была выпущена окончательно в течение трех лет (2008–2011). Это не в стиле Apache. Я ищу лучший вариант, но не нашел того, который очень популярен. Я хочу использовать что-то, что хорошо протестировано, не хочу сталкиваться с ошибками.

person mist    schedule 03.04.2011
comment
1.4 SNAPSHOT также требует Jakarta ORO. Apache Commons Validator мне не подходит. - person mist; 03.04.2011
comment
В итоге выбрал Dr.Vet. Решение Cumpanasu Florin: mkyong.com /регулярные-выражения/ - person mist; 04.04.2011
comment
Я согласен с тем, что валидатор Apache Commons работает хорошо, но я считаю его довольно медленным — более 3 мс на вызов. - person Nic Cottrell; 01.02.2012
comment
Производительность для меня не так важна. - person mist; 03.02.2012
comment
текущий транковый SNAPSHOT (SVN REV 1227719 на данный момент) больше не имеет внешних зависимостей, таких как ORO - вам даже больше не нужен весь модуль проверки - четыре класса org.apache.commons.validator.routines.EmailValidator, InetAddressValidator, DomainValidator и RegexValidator может работать автономно - person Jörg; 15.02.2012

Вы также можете проверить длину — электронные письма имеют длину не более 254 символов. Я использую валидатор Apache Commons, и он не проверяет это.

person minglis    schedule 09.11.2010
comment
Вид RFC 2821 (раздел 4.5.3.1) определяет local-part длину 64 и domain длина 255. (Говорят, что дольше разрешено, может быть отклонено другим программным обеспечением.) - person sarnold; 23.11.2011

Кажется, не существует идеальных библиотек или способов сделать это самостоятельно, если только вам не нужно время, чтобы отправить электронное письмо на адрес электронной почты и дождаться ответа (хотя это может быть и не вариант). В итоге я использовал предложение отсюда http://blog.logichigh.com/2010/09/02/validating-an-e-mail-address/ и скорректировать код, чтобы он работал на Java.

public static boolean isValidEmailAddress(String email) {
    boolean stricterFilter = true; 
    String stricterFilterString = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
    String laxString = ".+@.+\\.[A-Za-z]{2}[A-Za-z]*";
    String emailRegex = stricterFilter ? stricterFilterString : laxString;
    java.util.regex.Pattern p = java.util.regex.Pattern.compile(emailRegex);
    java.util.regex.Matcher m = p.matcher(email);
    return m.matches();
}
person matt.writes.code    schedule 30.09.2013

Это лучший метод:

public static boolean isValidEmail(String enteredEmail){
        String EMAIL_REGIX = "^[\\\\w!#$%&’*+/=?`{|}~^-]+(?:\\\\.[\\\\w!#$%&’*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\\\.)+[a-zA-Z]{2,6}$";
        Pattern pattern = Pattern.compile(EMAIL_REGIX);
        Matcher matcher = pattern.matcher(enteredEmail);
        return ((!enteredEmail.isEmpty()) && (enteredEmail!=null) && (matcher.matches()));
    }

Источники: - http://howtodoinjava.com/2014/11/11/java-regex-validate-email-address/

http://www.rfc-editor.org/rfc/rfc5322.txt

person Pravinsingh Waghela    schedule 15.10.2015

Другой вариант — использовать Hibernate. валидатор электронной почты, используя аннотацию @Email или используя класс валидатора программно, например:

import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator; 

class Validator {
    // code
    private boolean isValidEmail(String email) {
        EmailValidator emailValidator = new EmailValidator();
        return emailValidator.isValid(email, null);
    }

}
person Dherik    schedule 18.12.2017
comment
Почему минус? Это тот же класс, который используется Hibernate Validator. - person Dherik; 16.02.2018

Вот мой прагматичный подход, когда я просто хочу иметь разумные разные адреса blah@domain, используя допустимые символы из RFC. Адреса должны быть предварительно преобразованы в нижний регистр.

public class EmailAddressValidator {

    private static final String domainChars = "a-z0-9\\-";
    private static final String atomChars = "a-z0-9\\Q!#$%&'*+-/=?^_`{|}~\\E";
    private static final String emailRegex = "^" + dot(atomChars) + "@" + dot(domainChars) + "$";
    private static final Pattern emailPattern = Pattern.compile(emailRegex);

    private static String dot(String chars) {
        return "[" + chars + "]+(?:\\.[" + chars + "]+)*";
    }

    public static boolean isValidEmailAddress(String address) {
        return address != null && emailPattern.matcher(address).matches();
    }

}
person Craig Day    schedule 20.01.2016