Регулярные выражения в Java: от теории к практике
Регулярные выражения (regex) — мощный инструмент для поиска, замены и валидации текста. В Java работа с ними реализована через пакет java.util.regex, который включает два основных класса: Pattern (шаблон) и Matcher (сопоставитель). В этой статье мы разберём синтаксис, ключевые методы и закрепим знания на реальных примерах кода.
Материал будет полезен как новичкам, только знакомящимся с Java, так и опытным разработчикам, желающим освежить знания. Все примеры можно сразу запускать в вашей IDE.
1. Основы работы с Pattern и Matcher
Любая работа с regex в Java начинается с компиляции шаблона через Pattern.compile(). После этого создаётся объект Matcher, который применяет шаблон к строке.
Рассмотрим простейший пример: поиск всех цифр в тексте.
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexDemo {
public static void main(String[] args) {
String text = "Java версия 21 вышла в 2023 году";
String regex = "\\d+"; // одна или более цифр
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
System.out.println("Найдено: " + matcher.group());
}
}
}
Вывод программы:
Найдено: 21
Найдено: 2023
Обратите внимание на двойной слеш \\d — в Java строковый литерал требует экранирования обратной косой черты. Если в regex нужно указать \d, в коде пишем \\d.
2. Синтаксис регулярных выражений: метасимволы и квантификаторы
Чтобы эффективно использовать regex, важно запомнить основные метасимволы:
.— любой символ (кроме перевода строки);\\d— цифра (аналог[0-9]);\\w— буква, цифра или подчёркивание ([a-zA-Z0-9_]);\\s— пробельный символ;^— начало строки;$— конец строки.
Квантификаторы управляют количеством повторений:
+— один и более раз;*— ноль и более раз;?— ноль или один раз;{n}— ровно n раз;{n,m}— от n до m раз.
Пример: найдём все слова, состоящие только из латинских букв, длиной от 3 до 5 символов.
String text = "Java regex example for beginners";
String regex = "\\b[a-zA-Z]{3,5}\\b";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
System.out.println(matcher.group());
}
// Вывод: Java, regex, for
Граница слова \\b предотвращает частичное совпадение (например, "Java" не будет частью "JavaScript").
3. Практические примеры: валидация и извлечение данных
Перейдём к задачам, которые часто встречаются в реальной разработке.
3.1. Валидация email-адреса
Простейшая проверка: наличие символа "@" и домена.
public static boolean isValidEmail(String email) {
String regex = "^[\\w.-]+@[\\w.-]+\\.\\w{2,}$";
return Pattern.matches(regex, email);
}
public static void main(String[] args) {
System.out.println(isValidEmail("user@example.com")); // true
System.out.println(isValidEmail("invalid-email")); // false
}
Метод Pattern.matches() автоматически проверяет строку целиком (эквивалентно matcher.matches()).
3.2. Извлечение номеров телефонов
Допустим, в тексте встречаются номера в формате "+7 (999) 123-45-67".
String text = "Контакт: +7 (495) 123-45-67, резервный: +7 (999) 987-65-43";
String regex = "\\+7\\s?\\(\\d{3}\\)\\s?\\d{3}-\\d{2}-\\d{2}";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
System.out.println("Телефон: " + matcher.group());
}
// Вывод:
// Телефон: +7 (495) 123-45-67
// Телефон: +7 (999) 987-65-43
3.3. Замена текста по шаблону
Класс Matcher поддерживает замену с помощью replaceAll(). Заменим все цифры на звёздочки:
String text = "Пароль: 12345, код: 6789";
String result = text.replaceAll("\\d", "*");
System.out.println(result);
// Вывод: Пароль: *****, код: ****
4. Продвинутые возможности: группы и флаги
Группы позволяют извлекать части совпадения. Например, разберём строку с датой "2023-12-25" на год, месяц и день.
String date = "Сегодня 2023-12-25";
String regex = "(\\d{4})-(\\d{2})-(\\d{2})";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(date);
if (matcher.find()) {
System.out.println("Год: " + matcher.group(1)); // 2023
System.out.println("Месяц: " + matcher.group(2)); // 12
System.out.println("День: " + matcher.group(3)); // 25
}
Флаги изменяют поведение поиска. Например, Pattern.CASE_INSENSITIVE игнорирует регистр:
Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher("Java JAVA java");
while (matcher.find()) {
System.out.print(matcher.group() + " "); // Java JAVA java
}
Полезный флаг Pattern.MULTILINE заставляет якор