Известно, что регулярные выражения – это, по сути, шаблоны из символов, которые задают определённое правило поиска. И, среди прочего, с их помощью возможно осуществить проверку определённой строки или строк на соответствие некоему заранее заданному паттерну или стандарту.
Одним из наглядных и довольно простых примеров использования регулярных выражений в Java и не только является проверка проверка данных пользователя, ввёдённых при регистрации на определённом сайте на корректность. В первую очередь это касается адреса электронной почты, так как к нему всегда предъявляются определённые орфографические требования.

Таким образом, предлагаю разобрать частный случай применения регулярных выражений на примере простого Java-приложения, которое могло бы обрабатывать введённый пользователем адрес электронной почты.
Итак, в Java все классы, описывающие регулярные выражения, хранятся в пакете java.util.regex. Нам понадобятся два класса – Pattern и Matcher
Первый класс, как видно из его названия, описывает паттерн или шаблон, которому должены соответствовать введённые нами данные (в нашем случае – адрес электронной почты), второй – собственно сами данные.

public class RegularExpression 	{
	public static void main(String [] args) {
	Pattern pattern = Pattern.compile(“”);
	Matcher matcher = pattern.matcher(“”);
	boolean matches = matcher.matches();
					}
					} 

С помощью класса Pattern создается объект, который возвращается статическим методом compile(). У данного метода нет конструкторов и передается в этот метод строка, которая, собственно, и будет нашим шаблоном. Кроме того, к классе Pattern предусмотрен метод matcher, в который по параметрам передается другая строка – та, которую мы хотим проверить на соответствие вышеуказанному шаблону. С помощью этого метода создается экземпляр класса Matcher.
У класса Matcher же, в свою очередь, имеется метод matches(), возвращающий true в случае соответствия данных паттерну и flase, если данные не прошли проверку. Результат мы запишем в булевую переменную matches.
Для того, чтобы перейти, собственно, к самой важной части – самому шаблону и его синтаксису, необходимо условиться о части требований предъявляемых к адресу электронной почты. В общем случае адрес электронной почты должен:
  1. Состоять из двух частей, разделённых символом “@”.
  2. Левая часть должна состоять из английских букв или цифр, может содержать точки и тире, притом после точки или тире обязательно должна следовать как минимум одна буква.
  3. Правая часть должна содержать хотя бы одну точку в конце, после которой должны следовать от двух до четырёх букв.
  4. Начинаться обе части должны с буквенных символов.

Начнём проверку с левой части. Она содержит буквенные символы в количестве от одной до бесконечности (на самом деле, конечно, количество их ограничено, но для наглядности представим себе потенциально бесконечный email). Синтаксисом регулярных выражений это описывается следующим образом:
Pattern pattern = Pattern.compile(“[A-Za-z0-9]{1,}”);

Символы в квадратных скобках указывают интервал возможных буквенных значений.
В фигурных скобках мы описываем допустимое количество символов, указанных ранее. Слева от запятой указано минимальное значение (единица), справа – максимальное. Отсутствие значения, как в нашем случае, говорит о том, что количество символов не имеет максимального значения. Паттерн также может содержать и строго фиксированное число символов или не содежать его вовсе. В последнем случае символ может быть использован лишь один раз.
Далее в нашем паттерне может быть знак тире. “Может быть” означает, что символ будет присутствовать в паттерне либо один раз, либо ни разу, следовательно:
Pattern pattern = Pattern.compile(“[A-Za-z0-9]){1,}[\\-]{0,1}”);

В случае присутствия тире, как мы уже оговорили, после него в обязательном порядке должна следовать хотя бы одна буква, т.е. начальный паттерн повторяется. Символ тире обозначается как [\\-]:
Pattern pattern = Pattern.compile(“[A-Za-z0-9]{1,}[\\-]{0,1}[A-Za-z0-9]{1,}”);

Помимо этого, в строке также может присутствовать точка ([\\.]), после которой, опять-таки, обязан следовать буквенный символ:
Pattern pattern = Pattern.compile(“[A-Za-z0-9]{1,}[\\-]{0,1}[A-Za-z0-9]{1,}[\\.]{0,1}[A-Za-z0-9]{1,}”);

Так как описанный паттерн левой части должен повторяться, мы обозначим это в следующем виде:
Pattern pattern = Pattern.compile(“([A-Za-z0-9]{1,}[\\-]{0,1}[A-Za-z0-9]{1,}[\\.]{0,1}[A-Za-z0-9]{1,})+”);

Знак плюса после общей скобки означает, что паттерн может повторяться от одного раза до неопределённого количества раз.
Так как левую часть от правой отделяет собачка, мы указываем, что после левой части в обязательном порядке будет присутствовать этот символ:
Pattern pattern = Pattern.compile(“([A-Za-z0-9]{1,}[\\-]{0,1}[A-Za-z0-9]{1,}[\\.]{0,1}[A-Za-z0-9]{1,})+@”);

Правая часть паттерна должна содержать в себе уже упомянутый набор букв в количестве от одного до бесконечности с обязательным наличием точки в конце. Как и в предыдущем случае, паттерн до точки может повторяться:
Pattern pattern = Pattern.compile(“([A-Za-z0-9]{1,}[\\-]{0,1}[A-Za-z0-9]{1,}[\\.]{0,1}[A-Za-z0-9]{1,})+@([A-Za-z0-9]{1,}[\\-]{0,1}[A-Za-z0-9]{1,}[\\.]{0,1}[A-Za-z0-9]{1,})+[\\.]{1});

В конце паттерна должны вновь следовать символы, причём в количестве от двух до четырёх:
Pattern pattern = Pattern.compile(“([A-Za-z0-9]{1,}[\\-]{0,1}[A-Za-z0-9]{1,}[\\.]{0,1}[A-Za-z0-9]{1,})+@([A-Za-z0-9]{1,}[\\-]{0,1}[A-Za-z0-9]{1,}[\\.]{0,1}[A-Za-z0-9]{1,})+[\\.]{1}[a-z]{2,4}”);

Вот, собственно, и весь паттерн. Не маленький, не находите? К счастью, есть способ несколько сократить этот набор, сделав его более читабельным и лёгким для восприятия.
Для начала, существует способ одновременного выражения наличия тире или точки в паттерне. Вместо того, чтобы отдельно пропсывать все точки ([\\.]) и тире ([\\-]), их можно выразить единым символом — [\\.-]. Используя его, мы можем сократить паттерн до следующего:
Pattern pattern = Pattern.compile(“([A-Za-z0-9]{1,}[\\.-]{0,1}[A-Za-z0-9]{1,})+@([A-Za-z0-9]{1,}[\\.-]{0,1}[A-Za-z0-9]{1,})+[\\.]{1}[a-z]{2,4}”);

Также, существует символ, который может означать любую букву или цифру — \\w. Т.е. он способен заменить описание типа [A-Za-z0-9]:
Pattern pattern = Pattern.compile(“(\\w{1,}[\\.-]{0,1}\\w{1,})+@(\\w{1,}[\\.-]{0,1}\\w{1,})+[\\.]{1}[a-z]{2,4}”);

Так как знак плюса означает наличие символа в количестве от одного до бесконечности, описанное выше можно также свести до:
Pattern pattern = Pattern.compile(“(\\w+[\\.-]{0,1}\\w+)+@(\\w+[\\.-]{0,1}\\w+)+[\\.]{1}[a-z]{2,4}”);

Кроме того, наличие символа не более одного раза можно обозначить символом ?:
Pattern pattern = Pattern.compile(“(\\w+[\\.-]?\\w+)+@(\\w+[\\.-]?\\w+)+[\\.]{1}[a-z]{2,4}”);

Есть также символ, означающий наличие чего-либо в паттерне неопределённое количество раз, т.е. {0,}. Обозначается он как *. В итоговом варианте мы имеем следующее:
Pattern pattern = Pattern.compile(“\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*\\.\\w{2,4}”);

Подобный паттерн намного компактнее, чем то, к чему мы пришли ранее. Всё, что нам теперь остаётся – это реализовать формальную часть приложения, используя наш готовый паттерн и булевую переменную:
public class RegularExpression 	{
public static void main(String [] args) {
Pattern pattern = Pattern.compile(“\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*\\.\\w{2,4}”);
Matcher matcher = pattern.matcher(“”);
boolean matches = matcher.matches();
					}
					} 

Как именно использовать данную булевую переменную – это уже дело вкуса или возможностей. Ключевые же моменты уже готовы и дальнейшая доработка остаётся целиком за Вами.
Надеюсь, данное изожение было достаточно доступным. И конечно же, дерзайте.

Комментарии (23)


  1. kloppspb
    18.09.2015 17:46
    +9

    Уж сколько раз твердили миру…


    1. uvarovalexander
      18.09.2015 17:47
      -6

      Повторение — мать учения!


      1. kloppspb
        18.09.2015 17:53
        +13

        Тогда имело смысл свести всё к одной строке: не проверяйте e-mail регулярками! или упрощайте до одной собаки.


      1. aleksandy
        21.09.2015 14:38
        +1

        Повторение — мать непонимания.


        1. grossws
          21.09.2015 15:45
          +1

          И заикания…


  1. VioletGiraffe
    18.09.2015 17:47
    +7

    Отмечу тот факт, что e-mail адреса невозможно на 100% корректно валидировать регулярным выражением.
    Одно из обсуждений на SO: http://stackoverflow.com/a/201378/634821


    1. RPG
      18.09.2015 20:52

      А как насчёт PEG? У неё вроде возможности шире. Хотелось бы что-то практичное почитать по теме таких выражений, а то PCRE уже избиты вдоль и поперёк.


      1. kloppspb
        18.09.2015 21:03
        +6

        Единственный способ проверить корректность e-mail — отправить по нему письмо и получить ответ. Всё, других способов нет.

        А если хочется намекнуть пользователю на ошибку, чтобы он её исправил до отправки формы, достаточно проверить на один символ "@" плюс что-то до и после него. Никакие грамматики для этого не нужны :)

        В общем, речь о том, что для tutorial по регекспам пример выбран очень неудачный. К тому же с кучей ошибок внутри, полностью убивающим даже попытки применить его на практике.


        1. grossws
          18.09.2015 21:20
          -2

          Единственный способ проверить корректность e-mail — отправить по нему письмо и получить ответ. Всё, других способов нет.
          Тоже не всегда. Правильный ли адрес root@wqezxc? А если у меня в /etc/hosts и в mydomain (если брать postfix) прописан wqezxc?
          Id est это правильный подход для проверки адреса регистрации/оповещения и т. п.


          1. kloppspb
            18.09.2015 21:37
            +4

            >Правильный ли адрес root@wqezxc?

            С точки зрения RFC — вполне :)


            1. grossws
              18.09.2015 23:14

              И этот пример прекрасно иллюстрирует «единственность». Уж не знаю, хто не согласен…


  1. lair
    18.09.2015 17:51
    +2

    Во-первых, никаких орфографических требований к email не предъявляется.
    Во-вторых: stackoverflow.com/a/201378/1105881


  1. bigfatbrowncat
    18.09.2015 18:02
    +3

    Я просто оставлю это здесь:

    www.ex-parrot.com/pdw/Mail-RFC822-Address.html

    Ой… повторился. Пардон.


  1. maovrn
    18.09.2015 18:34
    +2

    Понятно, что цель статьи — показать работу с регурялными выражениями. Но пример получился очень неудачный, совершенно не годится для реальной валидации. Слишком много нюансов упущено. Например, мой реальный часто используемый ящик с символом подчеркивания в имени в пролете. Домены верхнего уровня .travel, .museum в пролете. Не говоря уж про хитрые, но валидные адреса типа maovrn@localhost, noreply@8.8.8.8


    1. Krypt
      19.09.2015 20:29

      Там ещё обратная крайность не учтена. Почта может висеть на домене верхнего уровня. Например, совершенно точно почта была на домене «to.»


  1. fleaump
    18.09.2015 18:53
    +4

    Pattern pattern = Pattern.compile(“\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*\\.\\w{2,4}”);

    agrrr

    домен в зоне .company и любые более 4х буквенные сразу бреются.


    1. uvarovalexander
      18.09.2015 18:57
      -6

      То, о чём идёт речь — очень частные случаи. Туториал нацелен не на то, прошу заметить. Это ж всё равно что сетовать на отсутствие лямбда-выражений в учебнике «Джава для начинающих»


      1. fleaump
        18.09.2015 18:58
        +7

        много народа из таких «хау ту», простым нагугливанием втыкают в продакшн решения.


      1. Krypt
        19.09.2015 20:16

        > очень частные случаи
        Плохой подход: половину всех возможных вариантов из множества «случаи» можно расценивать как «частные». Они очень редки и их очень много.


      1. kloppspb
        20.09.2015 19:33
        +1

        Вот как раз сегодня человек пожаловался: форма отправки не позволяет вводить адреса в рф.
        Нет чтобы пропустить и попробовать в реале, хрен там.
        Любая не-ascii сущность вводит движок в истерику ;)


  1. ZyXI
    18.09.2015 20:24

    А вы уверены, что экранировать точку внутри [] нужно? Все известные мне движки регулярных выражений (движок Java к ним не относится) этого не требуют. Довольно большое количество движков, наоборот, воспримет [\.] как (?:\\|\.): т.е. [\.] будет совпадать как с обратной косой чертой, так и с точкой.


  1. Krypt
    19.09.2015 19:23

    > Состоять из двух частей, разделённых символом “@”.
    > Левая часть должна состоять из английских букв или цифр, может содержать точки и тире, притом после точки или тире обязательно должна следовать как минимум одна буква.
    > Правая часть должна содержать хотя бы одну точку в конце, после которой должны следовать от двух до четырёх букв.
    > Начинаться обе части должны с буквенных символов.

    Вы неправы во всех четырёх пунктах:
    — В адресе может быть больше одной '@', хотя да, одна из собак делит адрес на две логические части
    — Левая часть может содержать практически любые символы, а так же пользующиеся популярностью теги, идущие после знака "+".
    — Правая часть, фактически, это что угодно, к чему возможно зарезовить путь средствами днс или обратиться напрямую
    — справа может оказаться ip-адрес, в том числе и ipv6
    Вот вам корректный адрес:: «correct\»@ddress"@localhost

    Фактически, вы можете быть уверенны только в том, что в email-адресе есть хотя бы одна '@'.


    1. grossws
      19.09.2015 20:12

      так же пользующиеся популярностью теги, идущие после знака "+".
      И то, интерпретация их в качестве тегов/thread-id и т. п. на усмотрение MTA/MDA.