Регулярні вирази RegEx у Java: приклади і опис роботи
Регулярні вирази у мовах програмування — потужний, але складний в опануванні інструмент. Тому не дивно, що багато хто відкладає його на потім або намагається використовувати по мінімуму.
Але це найкращий спосіб опрацювання текстів, а також метод розв’язання завдань, які пов’язані з пошуком, редагуванням, заміною тексту у файлах. Тому регулярні вирази варто знати. Сьогодні мова піде про регулярні вирази в популярній кросплатформенній мові Java.
Зміст
Регулярні вирази в Java
Регулярні вирази в Java — це певні послідовності символів, за допомогою яких можна обробляти рядки тексту за спеціальними синтаксичними шаблонами.
Приклад регулярних виразів має такий вигляд:
String regex="java"; // шаблон рядка "java"; String regex="\\d{3}"; // шаблон рядка з трьох цифрових символів;
Ця методика дає змогу дізнатися, чи входить певний символ або їхня послідовність у рядок. Інакше кажучи, це методика для обробки рядків.
Наприклад, вона може застосовуватися, якщо потрібно перевірити коректність введення електронної пошти. У ньому обов’язково має бути присутнім символ @, адреса домену, крапка після нього і доменна зона. Тобто система перевіряє, щоб адреса виглядала як [email protected] (сама адреса без лапок, зрозуміло), а не user.gmail.com або user@gmail,com, або навіть [email protected].
- Matcher — за його допомогою рядок перевіряється за шаблоном, що дає змогу знайти збіги в рядках, які подаються на вхід. Наприклад, в адресі електронної пошти.
- Pattern — видає вже скомпільовану версію регулярного виразу.
- PatternSyntaxException — у межах цього класу надаються винятки, які не перевіряються. За допомогою цього методу можна шукати. Детальніше поговоримо нижче.
Методи класу Pattern
Клас Java, який називається Pattern — це базове рішення для роботи з регулярними виразами в Java. Саме його використовують при підключенні RegEx до коду програми.
Цей клас можна використовувати двома різними способами (методами). По-перше, це Pattern.matches()
. Він застосовується для швидкої перевірки тексту на відповідність заданому регулярному виразу. Але він перевіряє тільки один текст або рядок.
По-друге, це Pattern.compile()
— скомпільований екземпляр Pattern. Ось його вже можна використовувати кілька разів, щоб скласти регулярний вираз для кількох текстів.
Наприклад:
String regex="java"; // шаблон рядка "java"; String regex="\\d{3}"; // шаблон рядка з трьох цифрових символів;
Методи класу Matcher
Ще один клас Java для регулярних виразів — Matcher (java.util.regex.Matcher) — він необхідний для пошуку декількох входжень того чи іншого набору символів у межах одного тексту, а також застосовується для пошуку в різних текстах, які подаються на вхід у межах одного шаблону. У ньому є низка своїх методів, які використовуються для роботи.
Серед основних відзначимо такі:
boolean matches()
: цей метод повертає значенняtrue
у разі збігу рядка з шаблоном;boolean find()
: цей метод повертає значенняtrue
у разі виявлення підрядка, що збігається з шаблоном, після чого перейде до нього;int start()
: цей метод повертає значення індексу відповідності;int end()
: використовуючи цей метод, можна отримати показники індексу відповідності;String replaceAll(String str)
: а ось цей метод виводить значення, яке задається підрядкомstr
при зміні основного рядка.
А ось такий вигляд має приклад із використанням Pattern і Matcher:
імпорт java.util.regex.Matcher; import java.util.regex.Pattern; public class Main { public static void main (String[] args) { Pattern pattern1 = Pattern.compile ("[x-z]+");//Пошук відбуватиметься від x до z включно. //Пошук відбуватиметься тільки за символами нижнього регістру. //Щоб вимкнути чутливість до регістру, можна використовувати Pattern.CASE_INSENSITIVE. Matcher matcher1 = pattern1.matcher ("x y z 1 2 3 4 "); System.out.println (matcher1.find()); //Пошук будь-якого збігу з шаблоном. //Виводиться значення true, оскільки в рядку є символи шаблону. Matcher matcher2 = pattern1.matcher ("X Y Z 1 2 3 4"); System.out.println (matcher2.find()); //Виводиться значення false. //Так як у рядку немає символів, що підходять за шаблоном. Шаблон pattern2 = Pattern.compile ("[a-zA-Z0-9]"); //Додається пошук за символами нижнього і верхнього регістру, а також цифр. Matcher matcher3 = pattern2.matcher ("A B C D X Y Z a b c d x y z 1 2 3 4"); System.out.println (matcher3.find()); //Виводиться значення true
Методи класу PatternSyntaxException
Ще один клас — PatternSyntaxException. Він виводить повідомлення про неперевірене виключення, якщо в синтаксисі регулярного виразу припустилися помилки. Йдеться про некоректні символи в шаблоні, наприклад, про друкарську помилку.
Клас оголошується ось так:
public class PatternSyntaxException extends IllegalArgumentException
У переліку методів класу відзначимо такі:
getDescription ()
: метод отримує опис помилок;int getIndex ()
: цей метод отримує індекс помилки;getMessage ()
: у цьому методі повертається багаторядковий рядок, що містить опис помилки, індекс, шаблон із помилкою та навіть візуально показує, де в шаблоні допущена помилка;getPattern ()
: цей метод, своєю чергою, отримує помилковий шаблон регулярного виразу.
Приклад класу має такий вигляд:
Жива демонстрація package com.tutorialspoint; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; public class PatternSyntaxExceptionDemo { private static String REGEX = "[""; private static String INPUT = "Собака каже мяу" + "Усі собаки кажуть мяу."; private static String REPLACE = "cat"; public static void main(String[] args) { try{ Pattern pattern = Pattern.compile(REGEX); // отримати об'єкт matcher Matcher matcher = pattern.matcher(INPUT); INPUT = matcher.replaceAll(REPLACE); } catch(PatternSyntaxException e){ System.out.println("PatternSyntaxException: "); System.out.println("Description: "+ e.getDescription()); System.out.println("Index: "+ e.getIndex()); System.out.println("Message: "+ e.getMessage()); System.out.println("Pattern: "+ e.getPattern()); } } }
Створення регулярних виразів у Java
Для створення регулярного виразу задіюються рядки або об’єкти класу String. Але далеко не кожен рядок автоматично може скомпілюватися в регулярний вираз. Він має бути відповідним чином написаний — з використанням синтаксису, який визначається в офіційних специфікаціях Java.
У цьому разі йдеться про використання букв, цифр і метасимволів, які мають спеціальне значення саме в рамках регулярних виразів.
Синтаксис регулярних виразів
Синтаксичні конструкції регулярних виразів, як сказано вище, охоплюють літерні, цифрові та метасимволи.
Серед них:
- Літерали — цим словом називають звичайні літери, які не мають іншого позначення. Тобто це стандартний алфавіт. Ці символи застосовуються для пошуку — приміром, якщо шаблон містить слово dog, то літерали задіють для пошуку, а у видачі опиняться результати, що починаються з поєднання літер dog. Тобто там будуть і dog, і doghunter.
- Метасимволи. Це, наприклад, квадратні дужки [] і циркумфлекс (). Також іноді їх можна називати спеціальними символами. Наприклад, якщо написати ( [ dog]] ), то система буде шукати все, що не містить у собі поєднання букв dog.
Для пошуку меж рядків, слів і тому подібних даних використовуються такі метасимволи:
^
— метасимвол початку оброблюваного рядка;$
— метасимвол закінчення оброблюваного рядка;-
— так задається метасимвол, який має бути поза дужками, причому він повинен бути тільки один;\b
— таким чином задається закінчення слова;\B
— так задається умова, коли слово не закінчене;\A
— так задається старт введення;\Z
— так задається закінчення введення.
Також можна вибирати певні класи символів:
\d
— будь-який цифровий символ;\D
— будь-який нецифровий;\s
— символ пробілу;\S\
— символ, що не має пробілу;.
— задати один довільний символ;\w
— абетково-цифровий символ;\W
— будь-який символ, за винятком абетково-цифрового.
Для завдання діапазону можна використовувати -
, у цьому разі регулярний вираз (a-z) шукатиме всі символи в заданому діапазоні.
Серед інших способів використання косої риски відзначимо:
\n
— перенесення рядка;\\+
— екранування символу, оскільки коса риска використовується як спецсимвол.
Детальніше про це поговоримо нижче у відповідному розділі.
Приклад найпростішого регулярного виразу для перевірки адреси електронної пошти на правильність має такий вигляд:
^[A-Z0-9+_.-]+@[A-Z0-9.-]+$
Тут перевіряється входження символу @
, крапки-роздільника і доменної зони, які є обов’язковими для електронної пошти.
А ось так виглядає складніший приклад, який шукає цифрові символи в рядку:
імпорт java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatches { public static void main( String args[] ) { // Рядок для сканування, щоб знайти шаблон String str = "Хрещення Київської Русі відбулося в 988 році! Чи не так?"; String pattern = "(.*)(\\d+)(.*)"; // Створення Pattern об'єкта Pattern r = Pattern.compile(pattern); // Створення matcher об'єкта Matcher m = r.matcher(str); if (m.find( )) { System.out.println("Знайдено значення: " + m.group(0)); System.out.println("Знайдено значення: " + m.group(1)); System.out.println("Знайдено значення: " + m.group(2)); }інакше { System.out.println("НЕ Збігається"); } } }
На виході отримаємо такий результат:
- Знайдено значення: Хрещення Київської Русі відбулося 988 року! Чи не так?
- Знайдено значення: Хрещення Київської Русі відбулося у 98
- Знайдено значення: 8
Квантифікатори, жадібний, наджадібний і ледачий режими
Крім символів у регулярних виразах застосовуються квантифікатори. Це обмежувачі, за допомогою яких задається частота появи певного символу або декількох. Їх потрібно записувати після самих символів.
Основні квантифікатори:
?
— символ не з’являється зовсім або ж з’являється лише раз;-
— символу зовсім немає в рядку або він з’являється, або з’являється більше ніж 0 разів;+
— символ з’являється щонайменше двічі або більшу кількість разів;{n}
— символ з’являється задане число разів (n разів);{n,}
— символ з’являється задане число разів з умовою (n і більше);{n,m}
— не менше n, але не більше m разів.
Квантифікатори можуть функціонувати в одному з трьох режимів: жадібному, наджадібному та ледачому. Базовий — жадібний режим — саме в ньому регулярні вирази працюють за замовчуванням.
У жадібному режимі програма буде шукати символи максимальної довжини, що збігаються в рамках рядка. Прохід здійснюється спочатку зліва направо, а потім справа наліво.
Наджадібний режим функціонує подібним чином, але він не використовує зворотний прохід (коли перевірка рядка йде справа наліво) — так званий реверсивний пошук. Це в деяких випадках дає змогу прискорити процес.
Лінивий режим шукає найкоротше входження символів, які збігаються, та задовольняє всім умовам, заданим у пошуковому шаблоні.
Давайте перейдемо до прикладів.
Ось такий вигляд має жадібний режим:
"A.+a"// Шукає максимальний за довжиною збіг у рядку. //Приклад жадібного квантифікатора import java.util.regex.Matcher; import java.util.regex.Pattern; public class Main { public static void main(String[] args) { Pattern p = Pattern.compile("a+"); Matcher m = p.matcher("aaa"); while (m.find()) System.out.println("Шаблон знайдено з " + m.start() + " to " + (m.end()-1))); } }
На виході буде таке:
Pattern found from 0 to 2
Наджадібний режим:
"А.++а"?"// Працює так само, як і жадібний режим, але не здійснює реверсивний пошук під час захоплення рядка. // Приклад наджадібного квантифікатора import java.util.regex.Matcher; import java.util.regex.Pattern; public class Main { public static void main(String[] args) { Pattern p = Pattern.compile("a++"); Matcher m = p.matcher("aaa"); while (m.find()) System.out.println("Шаблон знайдено з " + m.start() + " to " + (m.end()-1))); } }
На виході буде таке:
Pattern found from 0 to 2
Ледачий режим:
"A.+?a"// Шукає найкоротший збіг. //Приклад ледачого квантифікатора import java.util.regex.Matcher; import java.util.regex.Pattern; public class Main { public static void main(String[] args) { Pattern p = Pattern.compile("g+?"); Matcher m = p.matcher("ggg"); while (m.find()) System.out.println("Шаблон знайдено з " + m.start() + " to " + (m.end()-1))); } }
Висновок буде таким:
Pattern found from 0 to 0 Pattern found from 1 to 1 Pattern found from 2 to 2
Приклади використання регулярних виразів у Java
У цьому розділі ми розглянемо деякі приклади, які допоможуть зрозуміти, як все це працює на ділі.
Перевірка введеної адреси електронної пошти на коректність
Раніше ми згадували, що один із базових прикладів — перевірка адреси електронної пошти на валідність і коректність. Тобто щоб стояв символ @
, крапка і доменна зона.
Ось так ця перевірка має вигляд у коді:
List emails = new ArrayList(); emails.add("[email protected]"); //Неправильний імейл: emails.add("@gmail.com"); String regex = "^[A-Za-z0-9+_.-]+@(.+)$"; Pattern pattern = Pattern.compile(regex); for(String email : emails){ Matcher matcher = pattern.matcher(email); System.out.println(email +" : "+ matcher.matches()); } Результат: [email protected] : true @gmail.com : false
Перевірка телефонного номера
Цей регулярний вираз перевіряє валідність номера телефону. Виглядає він так:
import java.util.regex.Matcher; import java.util.regex.Pattern; public class ValidatePhoneNumber { public static void main(String[] argv) { String sPhoneNumber = "605-8889999"; // String sPhoneNumber = "605-88899991"; // String sPhoneNumber = "605-888999A"; Pattern pattern = Pattern.compile("\d{3}-\d{7}"); Matcher matcher = pattern.matcher(sPhoneNumber); if (matcher.matches()) { System.out.println("Phone Number Valid"); } іще { System.out.println("Номер телефону повинен бути у формі XXX-XXXXXXXXX"); } } }
Як можна перевірити мережеву адресу (IP-адресу) на правильність
А ось клас для визначення валідності IP-адреси, записаної в десятковому вигляді:
private static boolean checkIP(String input) { return input.matches("((0|1\\d{0,2}|2([0-4][0-9]|5[0-5]))\\.){3}(0|1\\d{0,2}|2([0-4][0-9]|5[0-5]))"); }
Перевірка на коректне число відкритих і закритих дужок у рядку
За правилами, кількість відкритих дужок має збігатися з кількістю закритих, оскільки в іншому випадку порушується логіка роботи. Це перевіряється так:
private static boolean checkExpression(String input) { Pattern pattern = Pattern.compile("\\([\\d+/*-]*\\)"); Matcher matcher = pattern.matcher(input); do { input = matcher.replaceAll(""); matcher = pattern.matcher(input); } while (matcher.find()); return input.matches("[\\d+/*-]*"); }
Витяг дати з рядка
Тепер спробуємо витягти дату з рядка. Це робиться таким чином:
private static String[] getDate(String desc) { int count = 0; String[] allMatches = new String[2]; Matcher m = Pattern.compile("(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\\d\\\d").matcher(desc); while (m.find()) { allMatches[count] = m.group(); count++; } return allMatches; }
Перевірка:
public static void main(String[] args) throws Exception{ String[] dates = getDate("coming from the 25/11/2020 to the 30/11/2020"); System.out.println(dates[0]); System.out.println(dates[1]); }
Результат:
25/11/2020 30/11/2020
Висновок
Як бачимо, регулярні вирази Java широко використовуються і дають змогу реалізовувати перевірки на коректність даних на льоту, хоча багато програмістів не люблять їх через складність. Але в умілих руках це потужний засіб для пошуку даних, роботи з рядками тощо.
Також ознайомитися з регулярними виразами можна у відео:
Favbet Tech – це ІТ-компанія зі 100% українською ДНК, що створює досконалі сервіси для iGaming і Betting з використанням передових технологій та надає доступ до них. Favbet Tech розробляє інноваційне програмне забезпечення через складну багатокомпонентну платформу, яка здатна витримувати величезні навантаження та створювати унікальний досвід для гравців.
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: