JavaRush /Java блог /Random UA /Основи регулярних виразів у Java. Частина 3
articles
15 рівень

Основи регулярних виразів у Java. Частина 3

Стаття з групи Random UA
Продовжуємо вивчення регулярних виразів. У цій статті ми розглянемо визначені класи символів та квантифікацію (пошук послідовностей). Основи регулярних виразів у 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 матчі з більш ніж трьома 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
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ