Java RegEx: применение регулярных выражений в коде
Рассмотрим регулярные выражения в Java, затронув синтаксис и наиболее популярные конструкции, а также продемонстрируем работу RegEx на примерах.
- Основы регулярных выражений
- Регулярные выражения в Java
- Примеры использования регулярных выражений в Java
Основы регулярных выражений
Мы подробно разобрали базис в статье Регулярные выражения для новичков, поэтому здесь пробежимся по основам лишь вскользь.
Определение
Регулярные выражения представляют собой формальный язык поиска и редактирования подстрок в тексте. Допустим, нужно проверить на валидность e-mail адрес. Это проверка на наличие имени адреса, символа @
, домена, точки после него и доменной зоны.
Вот самая простая регулярка для такой проверки:
^[A-Z0-9+_.-]+@[A-Z0-9.-]+$
В коде регулярные выражения обычно обозначается как regex, regexp или RE.
Синтаксис RegEx
Символы могут быть буквами, цифрами и метасимволами, которые задают шаблон:
Есть и другие конструкции, с помощью которых можно сокращать регулярки:
- \d — соответствует любой одной цифре и заменяет собой выражение [0-9];
- \D — исключает все цифры и заменяет [^0-9];
- \w — заменяет любую цифру, букву, а также знак нижнего подчёркивания;
- \W — любой символ кроме латиницы, цифр или нижнего подчёркивания;
- \s — поиск символов пробела;
- \S — поиск любого непробельного символа.
Квантификаторы
Это специальные ограничители, с помощью которых определяется частота появления элемента — символа, группы символов, etc:
?
— делает символ необязательным, означает0
или1
. То же самое, что и{0,1}
.*
—0
или более,{0,}
.+
—1
или более,{1,}
.{n}
— означает число в фигурных скобках.{n,m}
— не менееn
и не болееm
раз.*?
— символ?
после квантификатора делает его ленивым, чтобы найти наименьшее количество совпадений.
Примеры использования квантификаторов в регулярных выражениях
Обратите внимание, что квантификатор применяется только к символу, который стоит перед ним.
Также квантификаторов есть три режима:
"А.+а" //жадный режим — поиск самого длинного совпадения
"А.++а" //сверхжадный режим — как жадный, но без реверсивного поиска при захвате строки
"А.+?а" //ленивый режим — поиск самого короткого совпадения
По умолчанию квантификатор всегда работает в жадном режиме. Подробнее о квантификаторах в Java вы можете почитать здесь.
Java-разработчикМТС, Москва, можно удалённо, По итогам собеседованияtproger.ru
Примеры их использования рассмотрим чуть дальше.
Регулярные выражения в Java
Поскольку мы говорим о регекспах в Java, то следует учитывать спецификации данного языка программирования.
Экранирование символов в регулярных выражениях Java
В коде Java нередко можно встретить обратную косую черту \
: этот символ означает, что следующий за ним символ является специальным, и что его нужно особым образом интерпретировать. Так, \n
означает перенос строки. Посмотрим на примере:
String s = "Это спецсимвол Java. \nОн означает перенос строки.";
System.out.println(s);
Результат:
Это спецсимвол Java.
Он означает перенос строки.
Поэтому в регулярных выражениях для, например, метасимволов, используется двойная косая черта, чтобы указать компилятору Java, что это элемент регулярки. Пример записи поиска символов пробела:
String regex = "\\s";
Ключевые классы
Java RegExp обеспечиваются пакетом java.util.regex. Здесь ключевыми являются три класса:
- Matcher — выполняет операцию сопоставления в результате интерпретации шаблона.
- Pattern — предоставляет скомпилированное представление регулярного выражения.
- PatternSyntaxException — предоставляет непроверенное исключение, что указывает на синтаксическую ошибку, допущенную в шаблоне RegEx.
Также есть интерфейс MatchResult, который представляет результат операции сопоставления.
Примеры использования регулярных выражений в Java
e-mail адрес
В качестве первого примера мы упомянули регулярку, которая проверяет e-mail адрес на валидность. И вот как эта проверка выглядит в Java-коде:
List emails = new ArrayList();
emails.add("name@gmail.com");
//Неправильный имейл:
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());
}
Результат:
name@gmail.com : true
@gmail.com : false
Телефонный номер
Регулярное выражение для валидации номера телефона:
^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$
Эта регулярка ориентирована на российские мобильные номера, а также на городские с кодом из трёх цифр. Попробуйте написать код самостоятельно по принципу проверки e-mail адреса.
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
А вот использование различных режимов квантификаторов, принцип работы которых мы рассмотрели чуть ранее.
Жадный режим
Pattern pattern = Pattern.compile("a+");
Matcher matcher = pattern .matcher("aaa");
while (matcher.find()){
System.out.println("Найдено от " + matcher.start() +
" до " + (matcher.end()-1));
}
Результат:
Найдено от 0 дo 2
В заданном шаблоне первый символ – a
. Matcher сопоставляет его с каждым символом текста, начиная с нулевой позиции и захватывая всю строку до конца, в чём и проявляется его «жадность». Вот и получается, что заданная стартовая позиция – это 0, а последняя – 2.
Сверхжадный режим
Pattern pattern = Pattern.compile("a++");
Matcher matcher = pattern .matcher("aaa");
while (matcher.find()){
System.out.println("Найдено от " + matcher.start() +
" до " + (matcher.end()-1));
}
Результат:
Найдено от 0 дo 2
Принцип, как и в жадном режиме, только поиск заданного символа в обратном направлении не происходит. В приведённой строке всё аналогично: заданная стартовая позиция – это 0, а последняя – 2.
Ленивый режим
Pattern pattern = Pattern.compile("a+?");
Matcher matcher = pattern .matcher("aaa");
while (matcher.find()){
System.out.println("Найдено от " + matcher.start() +
" до " + (matcher.end()-1));
}
Результат:
Найдено от 0 дo 0
Найдено от 1 дo 1
Найдено от 2 дo 2
Здесь всё просто: самое короткое совпадение находится на первой, второй и третьей позиции заданной строки.
Выводы
Общий принцип использования регулярных выражений сохраняется от языка к языку, однако если мы всё-таки говорим о RegEx в конкретном языке программирования, следует учитывать его спецификации. В Java это экранирование символов, использование специальной библиотеки java.util.regex и её классов.