Matcher: примеры (JAVA)

Примеры работы с Matcher и пояснения
Раздел: Верификация (валидация), Регулярные выражения
matcher(CharSequence input): Matcher

Общее описание

Класс java.util.regex.Matcher служит для поиска соответствий регулярному выражению в заданной последовательности символов. Создание происходит через Pattern.matcher(CharSequence). Matcher применяется при необходимости пошагового поиска совпадений, извлечения групп, получения позиций совпадения и выполнения сложных замен с контролем процесса замены.

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

Ключевые методы, их назначение и возвращаемые значения:

  • boolean matches() - проверяет, полностью ли вход удовлетворяет шаблону; возвращает true или false.
  • boolean find() - ищет следующее совпадение, начиная с текущей позиции; возвращает true, если найдено совпадение.
  • boolean find(int start) - поиск следующего совпадения, начиная с указанной позиции.
  • boolean lookingAt() - проверяет, совпадает ли начало входа с шаблоном (частичное совпадение от начала).
  • String group() / String group(int) - возвращает текст всей группы или указанной захваченной группы; при отсутствии совпадения бросает IllegalStateException или возвращает null для несуществующей группы.
  • int start() / int end() - начальная и конечная позиции последнего совпадения.
  • int groupCount() - число захватывающих групп в шаблоне, возвращает целое.
  • Matcher reset() / Matcher reset(CharSequence) - сброс состояния поиска; возвращает сам объект Matcher.
  • String replaceAll(String replacement) / String replaceFirst(String replacement) - выполняет замену и возвращает изменённую строку.
  • appendReplacement(StringBuffer, String) и appendTail(StringBuffer) - низкоуровневый механизм поэтапной замены для сложной логики; возвращают и модифицируют буфер.
  • Matcher usePattern(Pattern) - смена шаблона для существующего Matcher; возвращает Matcher.
  • MatchResult toMatchResult() - возвращает неизменяемый результат последнего совпадения.
  • void region(int start, int end) - ограничение области поиска; не возвращает значение.
  • Matcher useTransparentBounds(boolean) и Matcher useAnchoringBounds(boolean) - управление поведением якорей и границ.
  • static String quoteReplacement(String s) (в Pattern) - экранирование строки замены для безопасного использования обратных ссылок.

Описание аргументов: обычно это примитивы int, булевы флаги и объекты String, Pattern, CharSequence. Исключения: IllegalStateException при обращении к группам без найденного совпадения, IndexOutOfBoundsException при неверных границах region, PatternSyntaxException при некорректном шаблоне.

Короткие примеры

Примеры наглядно демонстрируют основные варианты: полное совпадение, поиск фрагментов, группы и простая замена.

1) Полная проверка текста на соответствие шаблону

import java.util.regex.*;

Pattern p = Pattern.compile("\\d{3}-\\d{2}-\\d{4}");
Matcher m = p.matcher("123-45-6789");
System.out.println(m.matches());
true

2) Поиск всех совпадений и вывод групп

Pattern p = Pattern.compile("(\\w+)@(\\w+\\.\\w+)");
Matcher m = p.matcher("alice@mail.com bob@site.org");
while (m.find()) {
    System.out.println(m.group(0) + " -> user: " + m.group(1) + ", domain: " + m.group(2));
}
alice@mail.com -> user: alice, domain: mail.com
bob@site.org -> user: bob, domain: site.org

3) Простая замена с обратной ссылкой

Pattern p = Pattern.compile("(\\w+), (\\w+)");
Matcher m = p.matcher("Doe, John; Smith, Anna");
String result = m.replaceAll("$2 $1");
System.out.println(result);
John Doe; Anna Smith

4) Поэтапная замена с appendReplacement/appendTail

Pattern p = Pattern.compile("(\\d+)");
Matcher m = p.matcher("Items: 12, 5, 300");
StringBuffer sb = new StringBuffer();
while (m.find()) {
    int val = Integer.parseInt(m.group(1));
    m.appendReplacement(sb, String.valueOf(val * 2));
}
m.appendTail(sb);
System.out.println(sb.toString());
Items: 24, 10, 600

Альтернативы внутри Java

Несколько вариантов работы с шаблонами и их краткие особенности:

  • String.matches(String regex) - простой способ проверить полное совпадение; использует полный матч, не подходит для поиска подстрок.
  • Pattern.split(CharSequence) - разделение строки по шаблону; предпочтительно для разбора на части без явного перебора совпадений.
  • Класс Scanner - удобен для построчного чтения и простых шаблонов при разборе ввода; менее гибкий для сложных регулярных выражений.
  • Библиотеки вроде Apache Commons Lang / Google Guava - предлагают утилиты для регулярных операций, но для детального контроля лучше использовать Matcher.

Выбор зависит от задачи: для полного совпадения и короткой проверки - String.matches, для итеративного извлечения групп и сложных замен - Matcher, для разделения - Pattern.split.

Похожие возможности в других языках

Краткие примеры и отличия от Java:

PHP - preg_match_all и preg_replace:

$text = 'alice@mail.com bob@site.org';
preg_match_all('/(\w+)@(\w+\.\w+)/', $text, $m);
print_r($m[0]);
Array
(
    [0] => alice@mail.com
    [1] => bob@site.org
)

Отличие: синтаксис шаблонов с разделителями и модификаторами; функции возвращают массивы, нет отдельного объекта-итератора сравнимого с Matcher.

JavaScript - RegExp и методы строки:

const re = /(\w+)@(\w+\.\w+)/g;
const text = 'alice@mail.com bob@site.org';
let m;
while ((m = re.exec(text)) !== null) {
  console.log(m[0], m[1], m[2]);
}
alice@mail.com alice mail.com
bob@site.org bob site.org

Отличие: RegExp объект хранит позицию в lastIndex; поведение флагов и обратных ссылок похоже.

Python - модуль re:

import re
text = '123-45-6789'
print(bool(re.fullmatch(r'\d{3}-\d{2}-\d{4}', text)))
for m in re.finditer(r'(\w+)@(\w+\.\w+)', 'a@b.com c@d.org'):
    print(m.group(0), m.group(1), m.group(2))
True
a@b.com a b.com
c@d.org c d.org

Отличие: Python разделяет функции и объектные интерфейсы; поведение очень сходно, но синтаксис методов и обработка флагов отличается.

SQL - в разных СУБД доступны LIKE или REGEXP:

-- MySQL
SELECT * FROM users WHERE email REGEXP '[[:alnum:]]+@[[:alnum:]]+\.[[:alnum:]]+';
-- возвращает строки, удовлетворяющие регулярному выражению

Отличие: ограничения синтаксиса и производительность на больших наборах данных.

C# - System.Text.RegularExpressions.Regex:

using System.Text.RegularExpressions;
var m = Regex.Match("Doe, John", "(\\w+), (\\w+)");
Console.WriteLine(m.Success ? m.Groups[2].Value + " " + m.Groups[1].Value : "");
John Doe

Отличие: очень близкая модель к Java, но методы и объекты названы по-другому; есть дополнительные опции для быстрых сопоставлений.

Lua - встроенные функции string.match и gsub:

print(string.match("abc 123", "(%d+)"))
123

Отличие: упрощённый синтаксис шаблонов, не все regex-фичи поддерживаются.

Go - пакет regexp:

import "regexp"
re := regexp.MustCompile(`(\w+)@(\w+\.\w+)`)
for _, m := range re.FindAllStringSubmatch("a@b.com c@d.org", -1) {
    fmt.Println(m[0], m[1], m[2])
}
a@b.com a b.com
c@d.org c d.org

Отличие: Go использует синтаксис, близкий к POSIX/RE2, без поддержки некоторых сложных конструкций PCRE.

Kotlin - класс Regex поверх Java API:

val r = Regex("(\\w+)@(\\w+\\.\\w+)")
val matches = r.findAll("u@d.com v@t.org").map { it.groupValues }.toList()
println(matches)
[[u@d.com, u, d.com], [v@t.org, v, t.org]]

Отличие: Kotlin предоставляет более удобный API и расширения для работы с регулярными выражениями, но под капотом часто используется та же реализация Java.

Типичные ошибки

Распространённые проблемы и их примеры с результатом.

1) Использование matches() вместо find(), когда нужен поиск подстроки

Pattern p = Pattern.compile("test");
Matcher m = p.matcher("this is a test");
System.out.println(m.matches());
System.out.println(m.find());
false
true

Пояснение: matches() требует полного совпадения всей строки с шаблоном.

2) Неправильная нумерация групп (индексация начинается с 1 для захватывающих групп)

Pattern p = Pattern.compile("(a)(b)");
Matcher m = p.matcher("ab");
m.find();
System.out.println(m.group(0));
System.out.println(m.group(1));
System.out.println(m.group(2));
// System.out.println(m.group(3)); // вызовет исключение
ab
a
b

3) Игнорирование экранирования в строковых литералах

Pattern p = Pattern.compile("\d+\\.\d+"); // задумана дробь, но слеши перепутаны
// Правильно: "\\d+\\.\\d+"
PatternSyntaxException или некорректный шаблон в рантайме

4) Неправильное использование appendReplacement без appendTail

Pattern p = Pattern.compile("(\\d+)");
Matcher m = p.matcher("v1 2 v3");
StringBuffer sb = new StringBuffer();
while (m.find()) {
    m.appendReplacement(sb, "x");
}
// m.appendTail(sb); // если забыть, хвост строки не будет добавлен
System.out.println(sb.toString());
x (остаётся без хвоста, часть строки теряется)

5) Ошибки с region: неверные границы приводят к IndexOutOfBoundsException

Pattern p = Pattern.compile("a");
Matcher m = p.matcher("abc");
m.region(0, 10); // ошибка, т.к. конец выходит за длину
IndexOutOfBoundsException

Изменения и история

Класс Matcher и связанный Pattern введены в Java начиная с версии 1.4 и остаются основной реализацией регулярных выражений в JVM. В последующих релизах происходили в основном оптимизации производительности и исправления ошибок. Поддержка именованных групп доступна в современных версиях JVM и широко используется в коде.

Небольшие расширения и улучшения в API по большей части связаны с удобством использования в языках, работающих поверх JVM (например, Kotlin), а также с повышением устойчивости к ошибкам и исправлениями регресcий в реализации движка регулярных выражений.

Расширенные примеры

Несколько более сложных приёмов, где класс Matcher предоставляет мощный контроль над процессом.

1) Именованные группы и обращение к ним

Пример java
Pattern p = Pattern.compile("(?\\w+)@(?\\w+\\.\\w+)");
Matcher m = p.matcher("alice@mail.com");
if (m.find()) {
    System.out.println(m.group("name") + " -> " + m.group("domain"));
}
alice -> mail.com

2) Преобразование найденных чисел функционально с помощью appendReplacement

Пример java
Pattern p = Pattern.compile("(\\d+)");
Matcher m = p.matcher("values: 10, 200, 3");
StringBuffer sb = new StringBuffer();
while (m.find()) {
    int n = Integer.parseInt(m.group(1));
    // форматирование: числа с ведущими нулями до 4 знаков
    m.appendReplacement(sb, String.format("%04d", n));
}
m.appendTail(sb);
System.out.println(sb.toString());
values: 0010, 0200, 0003

3) Ограничение области поиска через region для парсинга сегментов

Пример java
Pattern p = Pattern.compile("\\w+");
Matcher m = p.matcher("first second third");
// искать только во втором слове
m.region(6, 12);
if (m.find()) System.out.println(m.group());
second

4) Lookahead/Lookbehind для поиска без включения в результат

Пример java
Pattern p = Pattern.compile("\\w+(?=:)\:");
Matcher m = p.matcher("key1:value1 key2:value2");
while (m.find()) System.out.println(m.group());
(зависит от шаблона; пример иллюстративен)

5) Сохранение неизменяемого результата для многопоточной проверки

Пример java
Pattern p = Pattern.compile("(\\w+)@(\\w+\\.\\w+)");
Matcher m = p.matcher("u@d.com");
if (m.find()) {
    MatchResult mr = m.toMatchResult();
    // mr можно безопасно передать в другой поток для чтения результата
    System.out.println(mr.group(1) + "@" + mr.group(2));
}
u@d.com

6) Использование флагов для многострочного и dotall поведения

Пример java
Pattern p = Pattern.compile("^start.*end$", Pattern.DOTALL | Pattern.MULTILINE);
Matcher m = p.matcher("start\nmiddle\nend");
System.out.println(m.find());
true

7) Преобразование результата с учётом экранирования замены

Пример java
String user = "$admin";
String replacement = Pattern.quoteReplacement(user);
Pattern p = Pattern.compile("\$\w+");
Matcher m = p.matcher("Hello $admin and $root");
System.out.println(m.replaceAll(replacement));
Hello $admin and $admin

Каждый пример иллюстрирует практические приёмы: именованные группы для читаемости, appendReplacement для нестандартных преобразований, region для локализованного поиска, флаги для управления поведением шаблона и безопасное экранирование строк замены.

джава matcher function comments

En
Matcher Creates a matcher that will match the given input against this pattern