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

Посмотреть в Telegram: @JavaSobes/188
Как мы уже писали ранее, основная функциональность работы с регулярными выражениями представлена в Java классом Matcher. Рассмотрим его подробнее.

Во-первых, в общем случае матчер действует не по всей строке, а только внутри заданного «региона». Изначально регион совпадает со всей строкой, но его можно сужать и изменять в процессе работы. Методы regionStart и regionEnd возвращают текущие границы, а region устанавливает новые.

Свойство transparentBounds экземпляра матчера может разрешать регулярному выражению заглядывать при поиске за границы, при условии что итоговая совпавшая подстрока будет всё ещё в границах региона (lookahead и lookbehind). А выключив свойство anchoringBounds можно перестать трактовать границы региона как границы строки (^ и $ в выражении).
Регулярные выражения используются для двух задач: поиска и замены. Поговорим о поиске.

Метод matches проверяет, удовлетворяет ли выражению весь регион, lookingAt – хотя бы его начало. Метод find похож на next итератора – он последовательно идет по строке, и находит следующие совпадения с выражением. Эту итерацию можно сдвинуть на определенную позицию строки, передав позицию как параметр.

Matcher реализует интерфейс MatchResult. Через него предоставляется информация о последнем успешном поиске (любым из перечисленных методов). Если эту информацию необходимо сохранить, toMatchResult() выделит её в отдельный иммутабельный объект. А если хочется обработать последовательность всех совпадений в виде стрима, поможет метод results().

Интерфейс MatchReslut предоставляет методы group, start и end. Они дают содержимое найденной подстроки и ее позицию в строке. Если этим методам параметром передать номер или имя группы, то результатом будет информация не о всей подстроке, а о ее группах. Общее количество групп хранится в свойстве groupCount.

Есть еще пара свойств последнего поиска, которые актуальны не только для успешного результата, поэтому не вошли в интерфейс: hitEnd и requireEnd. hitEnd сообщает, пришлось ли при последнем поиске дойти до конца региона. requireEnd подскажет, мог ли измениться результат (успех/неудача) последнего поиска, если бы в конец региона был добавлен хвост.

Метод reset сбрасывает всё это текущее состояние поиска. Передав в него параметр, можно заодно заменить строку, с которой работаем. Используемое регулярное выражение тоже можно заменить, методом usePattern, но состояние поиска при этом не сбросится.
Для замены совпавших с регулярным выражением подстрок есть методы replaceFirst и replaceAll. В них можно передать как строку-замену, так и коллбэк, который будет вычислять её на лету. Оба метода сбрасывают состояние.

При замене можно использовать $ для ссылки на группы совпадения, а символ \ используется для escape-последовательностей. Если требуется воспринимать эти символы без дополнительного смысла, необходимо обернуть строку-замену в вызов quoteReplacement.

Есть более гибкий способ замены. Matcher позволяет последовательно вручную выполнять поиск (всё теми же методами), а затем добавлять пройденный кусок строки с замененным совпадением в StringBuilder/StringBuffer методом appendReplacement. Оставшийся непройденный хвост добавляется методом appendTail. То есть, последовательность вызовов m.find(); m.appendReplacement(); m.appendTail(); эквивалентна вызову m.replaceFirst(), а while(m.find()) m.appendReplacement(); m.appendTail(); – это m.replaceAll().