JavaRush /Java блог /Архив info.javarush /Основы регулярных выражений в Java. Часть 3
articles
15 уровень

Основы регулярных выражений в Java. Часть 3

Статья из группы Архив info.javarush
Продолжаем изучение регулярных выражений. В этой статье мы рассмотрим предопределенные классы символов, а также квантификацию (поиск последовательностей). Основы регулярных выражений в Java. Часть 3 - 1

Предопределенные классы символов

API класса Pattern содержит предопределенные классы символов, предлагающие удобные сокращения часто используемых регулярных выражений. Основы регулярных выражений в Java. Часть 3 - 2В этой таблице конструкции в левой колонке — сокращенное представление выражений из правой колонки. Например, \d означает цифру (0-9), \w означает любую заглавную или прописную букву, символ подчеркивания или цифру). Используйте предопределенные классы символов, где это возможно. Это сделает ваш код проще для чтения и исправления ошибок. Конструкции, начинающиеся с обратного слеша, называются экранированными или защищенными. В предыдущих статьях мы уже говорили об экранировании специальных символов обратным слешем или символами \Q и \E для использования их в качестве обычных символов. Если вы используете обратный слеш с обычными символами (литералами), то необходимо экранировать этот бэк слеш, чтобы выражение скомпилировалось.

private final String REGEX = "\\d"; // цифра
В этом примере \d — регулярное выражение; дополнительный обратный слеш необходим для того, чтобы программа скомпилировалась. Наша тестовая программа читает регулярные выражения прямо из консоли, поэтому дополнительный слеш не нужен. Следующий пример демонстрирует использование предопределенных символьных классов: Основы регулярных выражений в Java. Часть 3 - 3Основы регулярных выражений в Java. Часть 3 - 4В первых трех примерах регулярное выражение — просто «.» (специальный символ точка), что означает любой символ. Поэтому поиск был успешным во всех случаях. В других примерах используются предопределенные символьные классы, значения которых мы рассмотрели в таблице выше.

Квантификаторы

Основы регулярных выражений в Java. Часть 3 - 4Квантификаторы позволяют задавать количество вхождений символа в строку. Рассмотрим подробнее тонкости работы жадных, ленивых и очень жадных квантификаторов. На первый взгляд может показаться, что квантификаторы X?, X?? и X?+ работают одинаково: «X есть один раз или нет совсем». Есть небольшие отличия в реализации этих квантификаторов, которые мы рассмотрим ниже.

Совпадения нулевой длины

Начнем с жадного. Напишем три разных регулярных выражения: буква «a» со специальными символами ?, * или +. Посмотрим, что произойдет если тестировать эти регулярки на пустой строке: Основы регулярных выражений в Java. Часть 3 - 5В примере выше, поиск был удачным в первых двух случаях, потому что выражения a? и a* допускают отсутствие символа a в строке. Также обратите внимание, что начальный и последний индексы совпадения одинаковы (0). Так как входная строка не имеет длины, программа находит ничего :) на первой же позиции. Этот случай называется совпадением нулевой длины. Такие совпадения встречаются в нескольких случаях: при пустой входной строке, в начале входной строки, после последнего символа строки или между символами строки. Совпадения нулевой длины легко обнаружить: они начинаются и заканчиваются на одной и той же позиции. Рассмотрим еще несколько примеров совпадений нулевой длины. Let’s explore zero-length matches with a few more examples. Изменим входную строку на символ «a» и наблюдаем интересный эффект: Основы регулярных выражений в Java. Часть 3 - 6Все три квантификатора нашли символ «a», но первые два, которые допускают отсутствие символа, нашли совпадение нулевой длины на позиции 1 — после последнего символа строки. Так происходит, потому что программа воспринимает символ «a» как строку и «бежит» по ней пока совпадения не закончатся. В зависимости от используемого квантификатора, программа будет или не будет находить «ничего» в конце строки. Теперь изменим входную строку на последовательность из пяти букв «a»: Основы регулярных выражений в Java. Часть 3 - 7Регулярное выражение a? находит совпадение для каждой буквы в строке отдельно. Выражение a* находит два совпадения: последовательность символов «a»‘ и совпадение нулевой длины на 5 позиции. И, наконец, регулярное выражение a+ находит только последовательность символов «a», не находя при этом «ничего» :) Что же будет происходить, если на вход подать строку, содержащую разные символы? Например, «ababaaaab»: Основы регулярных выражений в Java. Часть 3 - 8Символ «b» находится на 1, 3 и 8 позициях и программа находит совпадения нулевой длины на этих позициях. Регулярное выражение a? не обращает внимания на «b», а просто ищет присутствие (или отсутствие) символа «a». Если квантификатор допускает отсутствие «a» все символы в строке отличные от «a» будут показаны как совпадение нулевой длины. Для нахождения последовательностей заданной длины, просто укажите длину в фигурных скобках: Основы регулярных выражений в Java. Часть 3 - 9Регулярное выражение a{3} ищет последовательность из трех символов «a». В первой строке ничего не найдено, потому что в строке недостаточно символов a. Вторая содержит 3 символа, которые и находит программа. Третий тест также находит совпадение в начале строки. Все что находится за 3 символом не удовлетворяет регулярному выражению, в коде ниже — удовлетворяет и найдутся несколько совпадений: Основы регулярных выражений в Java. Часть 3 - 10Для указания минимальной длины последовательности — используйте:

Enter your regex: a{3,}
Enter input string to search: aaaaaaaaa
I found the text "aaaaaaaaa" starting at index 0 and ending at index 9.
В этом примере программа находит только одно совпадение, потому что строка удовлетворяет требованию минимальной длины последовательности (3) символов «a». Наконец, задание максимальной длины последовательности: Основы регулярных выражений в Java. Часть 3 - 11В этом примере первое совпадение закончилось на шестом символе. Второе совпадение содержит символы после шестого, т.к. они удовлетворяют требованию минимальной длины. Если бы строка была на один символ короче, второго совпадения не было бы.

Использование групп символов и классов с квантификаторами

До этого момента мы тестировали квантификаторы на строках, содержащих один и тот же символ. Квантификаторы распространяют свое действие только на один символ, поэтому регулярное выражение «abc+» будет находить стоки содержащие «ab» и «c» один или более раз. Оно не будет означать «abc» один или более раз. Но квантификаторы могут применяться вместе с группами и классами символов, например, [abc]+ (a или b или c, один или более раз) или (abc)+ («abc» один или более раз). Найдем группу символов (dog), три раза в строке: Основы регулярных выражений в Java. Часть 3 - 12В первом примере программа находит совпадение, т.к. квантификатор распространяется на группу символов. Если убрать скобки — квантификатор {3} будет распространяться только на букву «g». Так же можно применять квантификаторы с классами символов: Основы регулярных выражений в Java. Часть 3 - 13Квантификатор {3} распространяет действие на класс символов в скобках в первом примере, а во втором — только на символ «c».

Различия жадных, ленивых и сверхжадных квантификаторов

Есть небольшие различия между жадными (greedy), ленивыми (reluctant) и ревнивыми (possessive) квантификаторами. Жадные квантификаторы названы так, потому что пытаются найти максимально длинное совпадение: сначала программа пытается «съесть» всю строку, если совпадение не найдено, то отбрасывается один символ и поиск повторяется, пока не будет найдено совпадение или не останется больше символов. Ленивые — наоборот, начинают с начала строки, добавляя символ за символом, пока не найдут совпадение. Наконец, ревнивая квантификация просматривает сразу всю строку единожды, не убирая символы, как в жадной. Для демонстрации будем использовать строку xfooxxxxxxfoo. Основы регулярных выражений в Java. Часть 3 - 14Первый пример использует жадный квантификатор .* для поиска любого символа, 0 или более раз, за которым расположены символы "f" "o" "o". Так как кантификатор жадный — найденное совпадение содержит всю строку. Жадный квантификатор не найдет все совпадения в строке, т.к. на первом шаге, просмотрев всю строку, он найдет совпадение и закончит работу. Второй пример — ленивый и начинает с начала строки, добавляя символ за символом. Начинается работа программы с проверки «пустоты», но т.к. последовательности «foo» нет в начале строки, поиск продолжается с добавление символа «x», после которого будет найдено первое совпадение между индексами 0 и 4. Поиск продолжается до конца строки и второе совпадение будет найдено между индексами 4 и 13. Третий пример не находит совпадений потому что квантификатор ревнивый. В этом случае регулярное выражение .*+ «съело» всю строку не оставив ничего для «foo». Используйте ревнивый квантификатор, когда нужно отбросить все не нужное в строке, он будет эффективнее эквивалентного жадного квантификатора. На этом всё! Ссылка на первоисточник: Основы регулярных выражений в Java. Часть 3
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Радик Уровень 35
19 марта 2019
Так и не понял, для чего нужен "ревнивый" квантификатор.