W3docs

Группы и захваты в Java Regex

Захватывайте части совпадений в Java regex с помощью скобок, нумерованных и именованных групп.

Регулярное выражение не просто сообщает, совпадает ли строка — оно может разбить совпадение на части, которые можно считать. Эти части называются группами. Оборачивая часть шаблона в скобки, вы создаёте захватывающую группу, и после успешного выполнения Matcher вы можете извлечь каждую группу по номеру или по имени. В этой главе рассматриваются нумерованные группы, именованные группы, обратные ссылки, незахватывающие группы, а также их применение в заменах.

Нумерованные захватывающие группы

Каждая пара скобок в шаблоне открывает захватывающую группу, пронумерованную слева направо по открывающей (. Группа 0 является особой: она всегда соответствует всему совпадению. Таким образом, (\d{4})-(\d{2})-(\d{2}) даёт четыре группы — полную дату плюс год, месяц и день.

Pattern p = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
Matcher m = p.matcher("2026-05-30");
if (m.matches()) {
    System.out.println(m.group(0)); // 2026-05-30 (entire match)
    System.out.println(m.group(1)); // 2026
    System.out.println(m.group(2)); // 05
    System.out.println(m.group(3)); // 30
}

groupCount() возвращает количество захватывающих групп без учёта группы 0, поэтому приведённый выше шаблон возвращает 3. Обращение к несуществующему индексу группы приводит к IndexOutOfBoundsException.

Именованные группы

Подсчёт скобок становится ненадёжным по мере усложнения шаблонов. Именованные группы, записываемые как (?<name>...), позволяют читать захват по понятной метке, а не по индексу. Имена должны быть допустимыми Java-идентификаторами и уникальными в пределах шаблона.

Pattern p = Pattern.compile("(?<user>[\\w.]+)@(?<host>[\\w.]+)");
Matcher m = p.matcher("[email protected]");
if (m.matches()) {
    System.out.println(m.group("user")); // ada
    System.out.println(m.group("host")); // math.org
}

Именованные группы по-прежнему нумеруются под капотом, поэтому m.group(1) и m.group("user") возвращают один и тот же текст. Имя существует исключительно для удобства чтения.

Обратные ссылки

Обратная ссылка соответствует тому же тексту, который уже захватила предыдущая группа. Внутри шаблона пишется \1 для группы 1 (или \k<name> для именованной группы). Так можно обнаружить повторение — например, удвоенное слово — в пределах одного совпадения.

// \b(\w+)\s+\1\b  matches a word followed by the same word again
Pattern p = Pattern.compile("\\b(\\w+)\\s+\\1\\b");
Matcher m = p.matcher("the the end");
if (m.find()) {
    System.out.println(m.group(1)); // the
}

Обратите внимание на двойной обратный слеш в Java-коде: \\1 в строке превращается в \1 в фактическом регулярном выражении. Обратная ссылка может сработать только после того, как группа захватила текст, поэтому группа должна располагаться раньше в шаблоне.

Захватывающие группы в заменах

String.replaceAll, Matcher.replaceAll и appendReplacement — все они понимают ссылки на группы в тексте замены. Используйте $1, $2, ... для нумерованных групп и ${name} для именованных. Это превращает регулярные выражения в небольшой инструмент переупорядочивания и шаблонизации.

СсылкаЗначение в замене
$0Всё совпадение
$1, $2, ...Нумерованные захватывающие группы
${name}Именованная захватывающая группа
\$Символ доллара в буквальном виде
// Reorder "First Last" into "Last, First"
String out = "Ada Lovelace".replaceAll("(\\w+)\\s+(\\w+)", "$2, $1");
System.out.println(out); // Lovelace, Ada

Если в вывод нужно подставить буквальный $ или \, экранируйте их как \\$ или \\\\ в Java-строке.

Незахватывающие группы

Иногда скобки нужны только для группировки альтернации или применения квантификатора — без захвата. Незахватывающая группа (?:...) делает именно это: группирует без потребления номера группы, что делает индексы чистыми, а работу движка чуть быстрее.

// Group the protocol alternation, but capture only the host
Pattern p = Pattern.compile("(?:https?|ftp)://(\\S+)");
Matcher m = p.matcher("https://w3docs.com");
if (m.find()) {
    System.out.println(m.group(1)); // w3docs.com  (group 1, not 2)
}

Поскольку (?:https?|ftp) является незахватывающей группой, хост оказывается в группе 1, а не в группе 2. Необязательная захватывающая группа, не участвовавшая в совпадении, возвращает null, поэтому всегда проверяйте необязательные группы на null перед использованием.

Запускаемый пример

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

java— editable, runs on the server

Что следует вынести из запуска:

  • Две строки с датами показывают нумерованные группы: group(0) — всё совпадение, а group(1..3) — год, месяц и день, захваченные по позиции.
  • Строка с email доказывает, что именованные группы читают тот же текст, что и индексы, а groupCount=2 считает только именованные захваты, никогда не включая группу 0.
  • Doubled: the и Doubled: sat получены через обратную ссылку \1, соответствующую тому, что группа слова только что захватила — каждое повторяющееся слово находится независимо.
  • Swapped: Lovelace, Ada, Turing, Alan показывает переупорядочивание каждой пары имён с помощью $2, $1, а Masked: card [4111] [2222] демонстрирует именованную ссылку ${num} в шаблоне вывода.
  • Незахватывающая (?:https?|ftp) оставляет хост в группе 1 (w3docs.com), а необязательная группа дроби выводит frac=null, поскольку не участвовала в совпадении со строкой 42.

Что изучить дальше

Группы опираются на остальные возможности движка регулярных выражений, поэтому полезно хорошо разбираться в смежных темах:

  • Pattern и Matcher — классы, методы group(), groupCount() и replaceAll() которых используются здесь.
  • Квантификаторы Regex{4}, + и ? определяют, сколько захватывает каждая группа.
  • Классы символов Regex\d, \w и \S — это то, из чего состоит большинство групп.
  • Флаги Regex — флаги вроде CASE_INSENSITIVE изменяют то, что совпадает с вашими группами.

Практика

Практика
В шаблоне (?:https?|ftp)://(\S+) в какой группе находится часть хоста, захватываемая (\S+)?
В шаблоне (?:https?|ftp)://(\S+) в какой группе находится часть хоста, захватываемая (\S+)?
Was this page helpful?