Рубріки: Теория

Регулярные выражения RegEx в Java: примеры и описание работы

Андрей Галадей

Регулярные выражения в языках программирования — мощный, но сложный в освоении инструмент. Потому неудивительно, что многие откладывают его на потом или стараются использовать по минимуму.

Но это лучший способ обработки текстов, а также метод решения задач, которые связаны с поиском, редактированием, заменой текста в файлах. Потому регулярные выражения стоит знать. Сегодня речь пойдет о регулярных выражениях в популярном кроссплатформенном языке Java.

Регулярные выражения в Java

Регулярные выражения в Java — это определенные последовательности символов, с помощью которых можно обрабатывать строки текста по специальным синтаксическими шаблонам.

Пример регулярных выражений выглядит так:

String regex=”java”; // шаблон строки ”java”;
String regex=”\\d{3}”; // шаблон строки из трех цифровых символов;

Эта методика позволяет узнать, входит ли определенный символ или их последовательность в строку. Иначе говоря, это методика для обработки строк.

К примеру, она может применяться, если нужно проверить корректность ввода электронной почты. В нем обязательно должен присутствовать символ @, адрес домена, точка после него и доменная зона. То есть система проверяет, чтобы адрес выглядел как «user@gmail.com» (сам адрес без кавычек, разумеется), а не «user.gmail.com» или «user@gmail,com», или даже «user@gmail.com.».

  • Matcher — с его помощью строка проверяется по шаблону, что дает возможности найти совпадения в строках, которая подается на вход. Например, в адресе электронной почты.
  • Pattern — выдает уже скомпилированную версию регулярного выражения.
  • PatternSyntaxException — в рамках этого класса предоставляются непроверяемые исключения. С помощью этого метода можно искать. Подробнее поговорим ниже.

Методы класса Pattern

Класс Java, который называется Pattern — это базовое решение для работы с регулярными выражениями в Java. Именно его используют при подключении RegEx в код программы.

Этот класс можно использовать двумя разными способами (методами). Во-первых, это Pattern.matches(). Он применяется для быстрой проверки текста на соответствие заданному регулярному выражению. Но он проверяет только один текст или строку.

Во-вторых, это Pattern.compile() — скомпилированный экземпляр Pattern. Вот его уже можно использовать несколько раз, чтобы составить регулярное выражение для нескольких текстов.

Например:

String regex=”java”; // шаблон строки ”java”;
String regex=”\\d{3}”; // шаблон строки из трех цифровых символов;

Методы класса Matcher

Еще один класс Java для регулярных выражений — Matcher (java.util.regex.Matcher) — он необходим для поиска нескольких вхождений того или иного набора символов в рамках одного текста, а также применяется для поиска в разных текстах, которые подаются на вход в пределах одного шаблона. В нем есть ряд своих методов, которые используются для работы.

В числе основных отметим такие:

  • boolean matches(): этот метод возвращает значение true при совпадении строки с шаблоном;
  • boolean find(): этот метод возвращает значение true при обнаружении подстроки, которая совпадает с шаблоном, после чего перейдет к ней;
  • int start(): этот метод возвращает значение индекса соответствия;
  • int end(): используя этот метод, можно получить показатели индекса  соответствия;
  • String replaceAll(String str): а вот этот метод выводит значение, которое задается подстрокой str при изменении основной строки.

А вот так выглядит пример с использованием Pattern и Matcher:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main (String[] args) {
Pattern pattern1 = Pattern.compile ("[x-z]+");//Поиск будет происходить от x до z включительно.
//Поиск будет происходить только по символам нижнего регистра.
//Чтобы отключить чувствительность к регистру, можно использовать Pattern.CASE_INSENSITIVE.
Matcher matcher1 = pattern1.matcher ("x y z 1 2 3 4 ");
System.out.println (matcher1.find()); //Поиск любого совпадения с шаблоном.
//Выводится значение true, так как в строке есть символы шаблона.
Matcher matcher2 = pattern1.matcher ("X Y Z 1 2 3 4");
System.out.println (matcher2.find()); //Выводится значение false.
//Так как в строке нет символов, подходящих по шаблону.
Pattern pattern2 = Pattern.compile ("[a-zA-Z0-9]");
//Добавляется поиск по символам нижнего и верхнего регистра, а также цифр.
Matcher matcher3 = pattern2.matcher ("A B C D X Y Z a b c d x y z 1 2 3 4");
System.out.println (matcher3.find()); //Выводится значение true

Методы класса PatternSyntaxException

Еще один класс — PatternSyntaxException. Он выводит сообщение о непроверенном исключении, если в синтаксисе регулярного выражения допущена ошибка. Речь идет о некорректных символах в шаблоне, например, об опечатке.

Класс объявляется вот так:

public class PatternSyntaxException
extends IllegalArgumentException

В перечне методов класса отметим такие:

  • getDescription (): метод получает описание ошибок;
  • int getIndex (): этот метод получает индекс ошибки;
  • getMessage (): в этом методе возвращается многострочная строка, которая содержит описание ошибки, индекс, шаблон с ошибкой и даже визуально показывает, где в шаблоне допущена ошибка;
  • getPattern (): этот метод, в свою очередь, получает ошибочный шаблон регулярного выражения.

Пример класса выглядит так:

Live Demo
package com.tutorialspoint;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class PatternSyntaxExceptionDemo {
   private static String REGEX = "[";
   private static String INPUT = "The dog says meow " + "All dogs say meow.";
   private static String REPLACE = "cat";

   public static void main(String[] args) {
      try{
         Pattern pattern = Pattern.compile(REGEX);
      
         // get a matcher object
         Matcher matcher = pattern.matcher(INPUT);
         INPUT = matcher.replaceAll(REPLACE);
      } catch(PatternSyntaxException e){

         System.out.println("PatternSyntaxException: ");
         System.out.println("Description: "+ e.getDescription());
         System.out.println("Index: "+ e.getIndex());
         System.out.println("Message: "+ e.getMessage());
         System.out.println("Pattern: "+ e.getPattern());
      }
   }
}

Создание регулярных выражений в Java

Для создания регулярного выражения задействуются строки или объекты класса String. Но далеко не каждая строка автоматически может скомпилироваться в регулярное выражение. Она должна быть соответствующим образом написана — с использованием синтаксиса, который определяется в официальных спецификациях Java.

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

Синтаксис регулярных выражений

Синтаксические конструкции регулярных выражений, как сказано выше, включают буквенные, цифровые и метасимволы.

Среди них:

  • Литералы — этим словом называют обычные буквы, которые не имеют иного обозначения. То есть, это стандартный алфавит. Эти символы применяются для поиска — к примеру, если шаблон содержит слово dog, то литералы задействуют для поиска, а в выдаче окажутся результаты, которые начинаются с сочетания букв dog. То есть, там будут и dog, и doghunter.
  • Метасимволы. Это, к примеру, квадратные скобки [] и циркумфлекс (). Также иногда их можно называть специальными символами. Например, если написать ( [ dog] ), то система будет искать все, что не включает в себя сочетания букв dog.

Для поиска границ строк, слов и тому подобных данных используются следующие метасимволы:

  • ^ — метасимвол начала обрабатываемой строки;
  • $ — метасимвол окончания обрабатываемой строки;
  • - — так задается метасимвол, который должен быть вне скобок, причем он должен быть только один;
  • \b — таким образом задается окончание слова;
  • \B — так задается условие, когда слово не закончено;
  • \A — так задается старт ввода;
  • \Z — так задается окончание ввода.

Также можно выбирать определенные классы символов:

  • \d — любой цифровой символ;
  • \D — любой нецифровой;
  • \s — символ пробела;
  • \S — символ, не имеющий пробела;
  • . — задать один произвольный символ;
  • \w — буквенно-цифровой символ;
  • \W — любой символ, за исключением буквенно-цифрового.

Для задания диапазона можно использовать -, в этом случае регулярное выражение (a-z) будет искать все символы в заданном диапазоне.

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

  • \n — перенос строки;
  • \\+ — экранирование символа, поскольку косая черта используется в качестве спецсимвола. Подробнее об этом поговорим ниже в соответствующем разделе.

Пример самого простого регулярного выражения для проверки адреса электронной почты на правильность выглядит так:

^[A-Z0-9+_.-]+@[A-Z0-9.-]+$

Здесь проверяется вхождение символа @, точки-разделителя и доменной зоны, которые обязательны для электронной почты.

А вот так выглядит более сложный пример, который ищет цифровые символы в строке:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches {
public static void main( String args[] ) {
// Строка для сканирования, чтобы найти шаблон
String str = "Крещение Киевской Руси произошло в 988 году! Не так ли?";
String pattern = "(.*)(\\d+)(.*)";
// Создание Pattern объекта
Pattern r = Pattern.compile(pattern);
// Создание matcher объекта
Matcher m = r.matcher(str);
if (m.find( )) {
System.out.println("Найдено значение: " + m.group(0));
System.out.println("Найдено значение: " + m.group(1));
System.out.println("Найдено значение: " + m.group(2));
}else {
System.out.println("НЕ СОВПАДАЕТ");
}
}
}

На выходе получим следующий результат:

  • Найдено значение: Крещение Киевской Руси произошло в 988 году! Не так ли?
  • Найдено значение: Крещение Киевской Руси произошло в 98
  • Найдено значение: 8

Квантификаторы, жадный, сверхжадный и ленивый режимы

Помимо символов, в регулярных выражениях применяются квантификаторы. Это ограничители, с помощью которых задается частота появления определенного символа или нескольких. Их нужно записывать после самих символов.

Основные квантификаторы:

  • ? — символ не появляется вовсе или же появляется всего раз;
  • - — символа вовсе нет в строке или появляется или он появляется более чем 0 раз;
  • + — символ появляется не менее раза или большее число раз;
  • {n} — символ появляется заданное число раз (n раз);
  • {n,} — символ появляется заданное число раз с условием (n и больше);
  • {n,m} — не меньше n, но не более m раз.

Квантификаторы могут функционировать в одном из трех режимов: жадном, сверхжадном и ленивом. Базовый — жадный режим — именно в нем регулярные выражения работают по умолчанию.

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

Сверхжадный режим функционирует сходным образом, но он не использует обратный проход (когда проверка строки идет справа налево) — так называемый реверсивный поиск. Это в некоторых случаях позволяет ускорить процесс.

Ленивый режим ищет самое короткое вхождение совпадающих символов, которое удовлетворяет всем условиям, заданным в поисковом шаблоне.

Давайте перейдем к примерам.

Вот так выглядит жадный режим:

“A.+a”// Ищет максимальное по длине совпадение в строке.
//Пример жадного квантификатора
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main
{
public static void main(String[] args)
{
Pattern p = Pattern.compile("a+");
Matcher m = p.matcher("aaa");
while (m.find())
System.out.println("Pattern found from " + m.start() +
" to " + (m.end()-1));
}
}

На выходе будет следующее:

Pattern found from 0 to 2

Сверхжадный режим:

"А.++а"?// Работает также как и жадный режим, но не производит реверсивный поиск при захвате строки.

// Пример сверхжадного квантификатора
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main
{
public static void main(String[] args)
{
Pattern p = Pattern.compile("a++");
Matcher m = p.matcher("aaa");
while (m.find())
System.out.println("Pattern found from " + m.start() +
" to " + (m.end()-1));
}
}

На выходе будет следующее:

Pattern found from 0 to 2

Ленивый режим:

“A.+?a”// Ищет самое короткое совпадение.

//Пример ленивого квантификатора
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main
{
public static void main(String[] args)
{
Pattern p = Pattern.compile("g+?");
Matcher m = p.matcher("ggg");
while (m.find())
System.out.println("Pattern found from " + m.start() +
" to " + (m.end()-1));
}
}

Вывод будет таким:

Pattern found from 0 to 0

Pattern found from 1 to 1

Pattern found from 2 to 2

Примеры использования регулярных выражений в Java

В этом разделе мы рассмотрим некоторые примеры, которые помогут понять, как все это работает на деле.

Проверка введенного адреса электронной почты на корректность

Ранее мы упоминали, что один из базовых примеров — проверка адреса электронной почты на валидность и корректность. То есть чтобы стоял символ @, точка и доменная зона.

Вот так эта проверка выглядит в коде:

List emails = new ArrayList();
emails.add("name@gmail.com");
//Неправильный имейл:
emails.add("@gmail.com");
String regex = "^[A-Za-z0-9+_.-]+@(.+)$";
Pattern pattern = Pattern.compile(regex);
for(String email : emails){
Matcher matcher = pattern.matcher(email);
System.out.println(email +" : "+ matcher.matches());
}
Результат:
name@gmail.com : true
@gmail.com : false

Проверка телефонного номера

Это регулярное выражение проверяет валидность номера телефона. Выглядит оно так:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ValidatePhoneNumber {
public static void main(String[] argv) {
String sPhoneNumber = "605-8889999";
// String sPhoneNumber = "605-88899991";
// String sPhoneNumber = "605-888999A";
Pattern pattern = Pattern.compile("\d{3}-\d{7}");
Matcher matcher = pattern.matcher(sPhoneNumber);
if (matcher.matches()) {
System.out.println("Phone Number Valid");
}
else
{
System.out.println("Phone Number must be in the form XXX-XXXXXXX");
}
}
}

Как можно проверить сетевой адрес (IP-адрес) на правильность

А вот класс для определения валидности IP-адреса, записанного в десятичном виде:

private static boolean checkIP(String input) {

return input.matches("((0|1\\d{0,2}|2([0-4][0-9]|5[0-5]))\\.){3}(0|1\\d{0,2}|2([0-4][0-9]|5[0-5]))");

}

Проверка на корректное число открытых и закрытых скобок в строке

По правилам, количество открытых скобок должно совпадать с количеством закрытых, поскольку в ином случае нарушается логика работы. Это проверяется так:

private static boolean checkExpression(String input) {
Pattern pattern = Pattern.compile("\\([\\d+/*-]*\\)");
Matcher matcher = pattern.matcher(input);
do {
input = matcher.replaceAll("");
matcher = pattern.matcher(input);
} while (matcher.find());
return input.matches("[\\d+/*-]*");
}

Извлечение даты из строки

Теперь попробуем извлечь дату из строки. Это делается следующим образом:

private static String[] getDate(String desc) {
int count = 0;
String[] allMatches = new String[2];
Matcher m = Pattern.compile("(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\\d\\d").matcher(desc);
while (m.find()) {
allMatches[count] = m.group();
count++;
}
return allMatches;
}

Проверка:

public static void main(String[] args) throws Exception{
String[] dates = getDate("coming from the 25/11/2020 to the 30/11/2020");
System.out.println(dates[0]);
System.out.println(dates[1]);
}

Результат:

25/11/2020

30/11/2020

Заключение

Как видим, регулярные выражения Java широко используются и позволяют реализовывать проверки на корректность данных на лету, хотя многие программисты не любят их из-за сложности. Но в умелых руках это мощное средство для поиска данных, работы со строками и так далее.

Также ознакомиться с регулярными выражениями можно в видео:

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023