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

247

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

jon077
источник
15
Я просто оставлю это здесь: davidcelis.com/blog/2012/09/06/…
mpenkov
4
Текущий URL для Commons: commons.apache.org/proper/commons-validator/apidocs/org/apache/…
james.garriss
Вы не должны использовать библиотеки (или регулярные выражения), которые не проходят полную проверку. Из-за сложности действующего адреса электронной почты не существует промежуточного положения между отсутствием проверки и полной проверкой. Реализация Apache Commons не является всеобъемлющей. Мне известна только одна библиотека ( email-rfc2822-validator ), но она все еще работает с огромными регулярными выражениями. Комплексный лексер - это то, что вы действительно хотите. EmailValidator4J говорит, что это делает работу, но у меня нет опыта с этим.
Бенни Боттема
1
@BennyBottema Вместо того, чтобы редактировать вопрос с комментариями, сделайте пост в Meta, чтобы обсудить, почему это было закрыто, если у вас все еще есть вопросы.
Machavity

Ответы:

134

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

РЕДАКТИРОВАТЬ : произошла ошибка, из-за которой она слишком ограничивала домен, из-за чего он не принимал действительные электронные письма от новых доменов верхнего уровня.

Эта ошибка была устранена 03 / Jan / 15 02:48 в общей версии - версии 1.4.1

Мэтью Флэшен
источник
1
Я согласен с указанными вами дополнительными сведениями, но являются ли они частью проекта Commons Validation?
Duffymo
2
Нет, EmailValidatorкласс Apache не отправляет сообщение электронной почты для проверки.
Мэтью Флашен
3
Если ваш вариант использования заключается в проверке удаленного адреса электронной почты пользователя, это решение имеет существенный недостаток (аналог InternetAddress.validate ()): EmailValidator рассматривает пользователя @ [10.9.8.7] как действительные адреса электронной почты, которые соответствуют RFC, но, возможно, не для регистрации пользователя / контактной формы.
zillion1
1
@zillion, документированный в Apache COmmons: «Эта реализация не гарантирует отлов всех возможных ошибок в адресе электронной почты». И я сказал, что вы должны сделать, чтобы «убедиться, что это настоящая электронная почта». Однако адреса с локальными IP-адресами могут быть действительны в редких случаях.
Мэттью Флашен
5
У Apache Commons EmailValidator есть один серьезный недостаток: он не поддерживает IDN.
Piohen
261

Использовать официальный пакет 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;
}
Аарон Дэвидсон
источник
59
Обратите внимание, что InternetAddress.validate () считает user @ [10.9.8.7] и user @ localhost действительными адресами электронной почты, что и соответствует RFC. Хотя, в зависимости от варианта использования (веб-форма), вы можете рассматривать их как недействительные.
zillion1
8
не только это действует, как сказал @ zillion1, но и такие вещи, как bla @ bla, считаются действительными. На самом деле не лучшее решение.
Диего Пленц
4
@NicholasTolleyCottrell Это Java, здесь мы бросаем и ловим исключения, я не совсем понимаю вашу точку зрения
gyorgyabraham
17
Я подозреваю, что конструктор InternetAddress был подделан. Или моя система была подделана. Или RFC822 был подделан. Или я действительно мог бы немного поспать прямо сейчас. Но я только что попробовал некоторый код, и следующие пять строк все передаются как действительные адреса электронной почты, если вы передаете их конструктору InternetAddress, и «ясно», они недействительны. Здесь мы идем: ., .com, com., abcи 123. Кроме того, добавление начального или конечного пробела также не делает строки недействительными. Ты будешь судьей!
Мартин Андерссон
4
гм, сыр не работает должным образом, когда я запускаю его. на какую адскую библиотеку javax.mail вы ссылаетесь ???
Аарон Дэвидсон
91

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

pom.xml:

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

build.gradle:

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

Импорт:

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

Код:

String email = "myName@example.com";
boolean valid = EmailValidator.getInstance().isValid(email);

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

boolean allowLocal = true;
boolean valid = EmailValidator.getInstance(allowLocal).isValid(email);
Аксель Виллгерт
источник
2
В Android Studio вы можете добавить компиляцию 'commons-validator: commons-validator: 1.4.1' в зависимости вашего приложения \ build.gradle {}
Benjiko99
2
После того, как я действительно попытался построить мой проект, кажется, что Apache Commons не очень хорошо работает с Android, сотни предупреждений и некоторые ошибки, даже не компилировались. Это то, что я закончил тем, что использовал howtodoinjava.com/2014/11/11/java-regex-validate-email-address
Benjiko99
1
У меня та же проблема, что и у Benjiko99. После добавления зависимости проект не будет компилироваться, говорит, что java.exe завершил работу с ненулевым кодом выхода 2.
Амит Миттал
1
Я получал ошибки и в Android Studio. Я поменял с 1.4.1 на 1.5.1 и все работает!
Мэтт
1
Примечание: используйте Email_validator в org.apache.commons.validator.routines, так как EmailValidator в org.apache.commons.validator устарел (я использую Validator 1.6 commons)
HopeKing
71

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

    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();
    }

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

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

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

Пуджан Шривастава
источник
40
Это довольно жестко упрощенный валидатор, который игнорирует большинство правил RFC вместе с IDN. Я бы избежал этого для любого приложения качества продукции.
Mlaccetti
1
me@company.co.uk будет недействительным ...
Александр Буракевич
14
Не используйте свой собственный валидатор на основе регулярных выражений для вещей, охватываемых RFC.
Джош Гловер
6
изобретать велосипед - это нормально, если вы не против случайного
спущенного
это хорошо, но не для всех случаев.
Андрэйн
21

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

Pattern pattern = Pattern.compile("^.+@.+\\..+$");
Matcher matcher = pattern.matcher(email);
Александр Буракевич
источник
3
это действительно хороший момент, любое разумное приложение должно иметь другие меры для предотвращения использования этого входа в любом случае
jmaculate
4
Как насчет изменить его на «^. + @. + (\\. [^ \\.] +) + $», Чтобы избежать конечной точки?
Синхан Хуан,
7

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

Он основан на классе Les Hazlewood, но имеет множество улучшений и исправляет несколько ошибок. Лицензия Apache.

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

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

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

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

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

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

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

lacinato
источник
1
Привет, я скопировал его в GitHub для публичного сообщества с открытым исходным кодом. Теперь каждый может комментировать, документировать и улучшать код. github.com/bbottema/email-rfc2822-validator . Раньше я использовал старую версию Les, но мне пришлось удалить ее из-за ошибок замораживания регулярных выражений: leshazlewood.com/2006/11/06/emailaddress-java-class/…
Бенни Боттема
7

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

Маркус Малуш
источник
Хотя альтернатива Apache Commons, его реализация столь же проста, как и большинство библиотек на основе регулярных выражений. Из документов: «Однако, как обсуждается в этой статье, не обязательно практично реализовать 100% -ный совместимый валидатор электронной почты». Единственный исчерпывающий валидатор, основанный на регулярных выражениях, который я знаю, это email-rfc2822- validator, в противном случае EmailValidator4J выглядит многообещающим.
Бенни Боттема
5

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

Филипп
источник
1
Я опирался на превосходный класс Les Hazlewood (в котором есть некоторые ошибки). (См. Мой отдельный ответ на этот вопрос.) Несмотря на то, что я поддерживал метод регулярных выражений Java, мы прекрасно его используем в среде, критичной к производительности. Если все, что вы делаете, это парсинг адресов, производительность может быть проблемой, но для большинства пользователей я подозреваю, что это только начало того, что они делают. Мои обновления в классе также исправили ряд проблем с длительной рекурсией.
Лацинато
Это устаревшая библиотека, которая была дважды заменена, наконец, email-rfc2822-validator . Хотя он по-прежнему отвечает всем современным потребностям, он также склонен к скрытым ошибкам в производительности (и не поддерживает ограниченные изменения в новых спецификациях RFC).
Бенни Боттема
3

Я портировал часть кода в 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 с шаблонами регулярных выражений для разных tlds (слишком большой, чтобы включать :)

<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>
Эрик Мартинес
источник
Этот ответ больше не применим по понятным причинам. Удалите проверку TLD, и это, вероятно, приемлемо, если вы хотите принимать адреса электронной почты не на английском языке.
Кристофер Шнайдер
3
public class Validations {

    private Pattern regexPattern;
    private Matcher regMatcher;

    public String validateEmailAddress(String emailAddress) {

        regexPattern = Pattern.compile("^[(a-zA-Z-0-9-\\_\\+\\.)]+@[(a-z-A-z)]+\\.[(a-zA-z)]{2,3}$");
        regMatcher   = regexPattern.matcher(emailAddress);
        if(regMatcher.matches()) {
            return "Valid Email Address";
        } else {
            return "Invalid Email Address";
        }
    }

    public String validateMobileNumber(String mobileNumber) {
        regexPattern = Pattern.compile("^\\+[0-9]{2,3}+-[0-9]{10}$");
        regMatcher   = regexPattern.matcher(mobileNumber);
        if(regMatcher.matches()) {
            return "Valid Mobile Number";
        } else {
            return "Invalid Mobile Number";
        }
    }

    public static void main(String[] args) {

        String emailAddress = "suryaprakash.pisay@gmail.com";
        String mobileNumber = "+91-9986571622";
        Validations validations = new Validations();
        System.out.println(validations.validateEmailAddress(emailAddress));
        System.out.println(validations.validateMobileNumber(mobileNumber));
    }
}
Сурьяпракаш Писай
источник
2

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

Брайан Агнью
источник
2

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

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

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

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

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

Бенни Боттема
источник
1

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

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

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

RENES
источник
Я не уверен, что регулярное выражение - лучший способ сделать это, это совершенно нечитаемо, если вы собираетесь следовать RFC до буквы
user2813274
Согласитесь с @ user2813274, вам нужен правильный лексер, а не регулярное выражение для спагетти.
Бенни Боттема
1

Текущая версия 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. Я ищу лучший вариант, но не нашел того, который был бы принят. Я хочу использовать что-то, что хорошо проверено, не хочу попадать в ошибки.

туман
источник
1.4 SNAPSHOT также требует Jakarta ORO. Apache Commons Validator мне не подходит.
тумана
Наконец выбрал Dr.Vet. Решение Кумпанасу Флорина: mkyong.com/regular-expressions/…
туман
1
Я согласен, что валидатор Apache Commons работает хорошо, но я считаю, что он довольно медленный - более 3 мс на звонок.
Ник Коттрелл
Производительность не так важна для меня.
тумана
текущий транк SNAPSHOT (на данный момент SVN REV 1227719) больше не имеет внешних зависимостей, таких как ORO - вам больше не нужен весь модуль проверки - четыре класса org.apache.commons.validator.routines.EmailValidator, InetAddressValidator, DomainValidator и RegexValidator могут стоять в одиночестве
Йорг
0

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

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

Кажется, не существует идеальных библиотек или способов сделать это самостоятельно, если только у вас нет времени, чтобы отправить электронное письмо на адрес электронной почты и дождаться ответа (хотя это может быть и не вариант). В итоге я воспользовался предложением 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();
}
matt.writes.code
источник
-2

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

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

Правинсингх Вагела
источник
-2

Другой вариант - использовать валидатор электронной почты 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);
    }

}
Dherik
источник
Почему отрицательный голос? Это тот же класс, который используется в Hibernate Validator.
Дерик
-3

Вот мой прагматичный подход, когда я просто хочу разумные отличные адреса 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();
    }

}
День крейга
источник