Введение в регулярные выражения Java
Введение в регулярные выражения Java с пакетом java.util.regex: Pattern, Matcher и основной синтаксис.
Регулярное выражение (regex) — это компактный шаблон, описывающий множество строк. В Java пакет java.util.regex превращает эти шаблоны в небольшой быстрый движок сопоставления, который можно применять к любому тексту — для проверки ввода, поиска подстрок, извлечения полей или преобразования содержимого. В этой главе описаны ключевые элементы, прежде чем вы начнёте писать собственные шаблоны.
Что такое регулярное выражение
Regex — это просто строка, написанная с использованием специального синтаксиса, но Java не интерпретирует её посимвольно каждый раз. Вместо этого шаблон компилируется один раз в объект Pattern, а затем применяется к входным данным через Matcher. Скомпилированная форма представляет собой эффективный конечный автомат, поэтому повторное использование одного Pattern для многих входных данных гораздо дешевле повторной компиляции.
Шаблоны описывают структуру: литерал cat соответствует именно этим буквам, а метасимволы описывают формы — \d означает любую цифру, + — «одно или несколько», . — «любой символ». Комбинируя их, вы можете описать номера телефонов, адреса электронной почты или строки журнала в одной строке кода.
import java.util.regex.Pattern;
public class FirstPattern {
public static void main(String[] args) {
// Compile the pattern once; reuse the result.
Pattern digits = Pattern.compile("\\d+");
System.out.println(digits.matcher("abc123").find()); // true
System.out.println(digits.matcher("hello").find()); // false
}
}Обратите внимание на двойной обратный слеш: \d в regex должен быть записан как \\d в строковом литерале Java, потому что компилятор Java сначала поглощает один обратный слеш.
Pattern и Matcher
Почти всю работу выполняют два класса. Pattern — это скомпилированный, многократно используемый, потокобезопасный шаблон. Matcher — это состоятельный движок, запускающий этот шаблон против конкретного входного значения: он отслеживает текущую позицию в тексте, какие группы захвачены и где находилось последнее совпадение. Создавайте новый Matcher для каждого входного значения; никогда не используйте один совместно в нескольких потоках.
| Тип | Роль |
|---|---|
Pattern | Скомпилированный шаблон. Неизменяемый, потокобезопасный, многократно используемый. |
Matcher | Применяет Pattern к одному входному значению. Хранит состояние совпадения. |
Pattern.compile(regex) | Создаёт Pattern из строки regex. |
pattern.matcher(input) | Возвращает Matcher, привязанный к этому входному значению. |
String.matches(regex) | Вспомогательный метод одноразового использования, компилирующий и полностью сопоставляющий за один вызов. |
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PatternAndMatcher {
public static void main(String[] args) {
Pattern p = Pattern.compile("\\w+@\\w+\\.\\w+");
Matcher m = p.matcher("ping me at [email protected] please");
if (m.find()) {
System.out.println("Found email: " + m.group());
}
}
}find() против matches()
Самая распространённая ошибка новичков — путаница между двумя способами запуска шаблона. matches() требует, чтобы всё входное значение совпадало с шаблоном от начала до конца. find() сканирует любую подстроку, которая совпадает, и может вызываться многократно для обхода всех вхождений. lookingAt() находится между ними: он привязывается к началу, но не требует совпадения до конца.
| Метод | Привязан к началу? | Должен совпасть до конца? | Повторяемый? |
|---|---|---|---|
matches() | да | да | нет |
lookingAt() | да | нет | нет |
find() | нет | нет | да |
import java.util.regex.Pattern;
public class FindVsMatches {
public static void main(String[] args) {
Pattern p = Pattern.compile("\\d+");
System.out.println(p.matcher("42").matches()); // true (whole input)
System.out.println(p.matcher("age 42").matches()); // false (extra text)
System.out.println(p.matcher("age 42").find()); // true (substring)
System.out.println(p.matcher("age 42").lookingAt()); // false (no digit at start)
}
}Общий синтаксис для построения шаблонов
Большинство реальных шаблонов собираются из небольшого набора строительных блоков: классов символов, предопределённых сокращений, квантификаторов и якорей. Знание этих элементов охватывает большую часть задач. Каждая конструкция рассматривается в отдельной главе — см. Синтаксис regex, классы символов и квантификаторы для получения полной информации.
| Конструкция | Значение | Пример |
|---|---|---|
. | Любой одиночный символ (кроме символа новой строки) | a.c совпадает с abc, axc |
\d \w \s | Цифра, символ слова, пробельный символ | \d\d совпадает с 42 |
[abc] | Любой из символов a, b, c | [aeiou] совпадает с гласной |
[^abc] | Любой символ кроме a, b, c | [^0-9] совпадает с нецифровым символом |
* + ? | Ноль и более, один и более, ноль или один | ab+ совпадает с ab, abb |
{n} {n,m} | Ровно n, от n до m | \d{3} совпадает с 555 |
^ $ | Начало, конец ввода/строки | ^Hi совпадает с начальным Hi |
(...) | Захватывающая группа | (\d{4}) захватывает четыре цифры |
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GroupsExample {
public static void main(String[] args) {
// Two capturing groups: year and month.
Pattern date = Pattern.compile("(\\d{4})-(\\d{2})");
Matcher m = date.matcher("Released 2025-11 to users");
if (m.find()) {
System.out.println("Full: " + m.group(0)); // 2025-11
System.out.println("Year: " + m.group(1)); // 2025
System.out.println("Month: " + m.group(2)); // 11
}
}
}Практический пример
Программа ниже компилирует один шаблон номера телефона и демонстрирует весь API на его основе: она обходит каждое совпадение с помощью find(), считывает захватывающие группы и позиции совпадений, сравнивает find() с matches() и перезаписывает текст с помощью replaceAll(). Запустите её, чтобы увидеть движок в действии.
Что следует вынести из результатов:
find()вызывается в цикле и возвращает два совпадения, поэтому один и тот жеMatcherобходит текст по одному вхождению за раз, пока не вернётfalse.group(1)иgroup(2)возвращают части в скобках (555и1234), аgroup()без аргумента возвращает всё совпадение целиком.start()иend()возвращают смещения символов каждого совпадения, что позволяет выделить или вырезать нужную часть исходного текста.matches()на полном предложении выводитfalse, потому что шаблон не охватывает всю строку, тогда как"555-1234"отдельно выводитtrue— подтверждение того, чтоmatches()работает только с полным входным значением.replaceAll("XXX-XXXX")перезаписывает каждое совпадение за один проход, создавая маскированное предложение и показывая, как шаблоны управляют преобразованием текста.
Что дальше
Теперь, когда вы знаете ключевые компоненты — Pattern, Matcher и разницу между find() и matches() — углубитесь в тематические главы:
- Pattern и Matcher — полный API для компиляции и запуска шаблонов.
- Синтаксис regex — все метасимволы и экранирующие последовательности с объяснениями.
- Классы символов — наборы, диапазоны и отрицание.
- Квантификаторы — жадное, ленивое и притяжательное повторение.
- Захватывающие группы — извлечение и обратные ссылки на части совпадений.
- Флаги — нечувствительность к регистру, многострочный режим и другие модификаторы.