Регулярные выражения в Kotlin: от основ до продвинутых техник
Регулярные выражения (regex) — это мощный инструмент для поиска, извлечения и замены текста по шаблону. В Kotlin работа с регулярными выражениями реализована через класс Regex, который предоставляет богатый API для всех стандартных операций. В этой статье мы разберем синтаксис, основные функции и практические примеры использования регулярных выражений в Kotlin.
1. Создание и базовый синтаксис Regex в Kotlin
В Kotlin регулярное выражение создается с помощью класса Regex или через расширение .toRegex() для строк. Поддерживаются все стандартные метасимволы и квантификаторы.
Способы создания Regex:
// Способ 1: через конструктор
val regex = Regex("\\d{3}-\\d{2}-\\d{4}")
// Способ 2: через toRegex()
val pattern = "[a-z]+@[a-z]+\\.[a-z]+".toRegex()
// Способ 3: с опциями (регистронезависимый)
val caseInsensitive = Regex("kotlin", RegexOption.IGNORE_CASE)
Основные метасимволы и квантификаторы:
.— любой символ (кроме перевода строки)\d— цифра (0-9)\w— буква, цифра или подчеркивание\s— пробельный символ^— начало строки$— конец строки*— 0 или более повторений+— 1 или более повторений?— 0 или 1 повторение{n,m}— от n до m повторений
2. Основные операции: поиск, проверка и извлечение
Класс Regex предоставляет несколько методов для работы с текстом. Рассмотрим самые важные из них.
Проверка соответствия (matches)
Метод matches() проверяет, соответствует ли вся строка шаблону полностью:
val phonePattern = Regex("\\+7\\d{10}")
println(phonePattern.matches("+79261234567")) // true
println(phonePattern.matches("+7926123456")) // false (9 цифр вместо 10)
Поиск первого совпадения (find)
Метод find() возвращает первое совпадение в виде объекта MatchResult?:
val text = "Контакты: email@test.com и support@domain.org"
val emailRegex = "[\\w.]+@[\\w.]+\\.[a-z]+".toRegex()
val match = emailRegex.find(text)
println(match?.value) // email@test.com
println(match?.range) // 10..23 (индексы начала и конца)
Поиск всех совпадений (findAll)
Для получения всех совпадений используйте findAll(), который возвращает последовательность Sequence<MatchResult>:
val allMatches = emailRegex.findAll(text)
allMatches.forEach { match ->
println("Найдено: ${match.value} в позиции ${match.range.first}")
}
// Вывод:
// Найдено: email@test.com в позиции 10
// Найдено: support@domain.org в позиции 28
Проверка на наличие (containsMatchIn)
Метод containsMatchIn() возвращает Boolean — есть ли хотя бы одно совпадение:
val hasEmail = emailRegex.containsMatchIn(text)
println(hasEmail) // true
3. Замена и split с помощью Regex
Регулярные выражения отлично подходят для замены частей строки и разделения текста.
Замена первого совпадения (replaceFirst)
val original = "ID: 12345, код: 67890"
val digitRegex = "\\d+".toRegex()
val masked = digitRegex.replaceFirst(original, "***")
println(masked) // ID: ***, код: 67890
Замена всех совпадений (replace)
val censored = digitRegex.replace(original, "***")
println(censored) // ID: ***, код: ***
Замена с использованием лямбды
Вы можете передать лямбду, которая будет вызываться для каждого совпадения:
val text = "Цены: 100р, 250р, 500р"
val priceRegex = "(\\d+)р".toRegex()
val result = priceRegex.replace(text) { match ->
val price = match.groupValues[1].toInt()
"${price * 1.2}р (с НДС)"
}
println(result) // Цены: 120.0р (с НДС), 300.0р (с НДС), 600.0р (с НДС)
Разделение строки (split)
val data = "яблоко,груша;банан|апельсин"
val separators = "[,;|]".toRegex()
val fruits = separators.split(data)
println(fruits) // [яблоко, груша, банан, апельсин]
4. Группы захвата и именованные группы
Группы захвата позволяют извлекать части совпадения. В Kotlin группы нумеруются с 1 (группа 0 — все совпадение).
Нумерованные группы
val dateRegex = "(\\d{2})-(\\d{2})-(\\d{4})".toRegex()
val dateStr = "Дата: 15-03-2025"
val match = dateRegex.find(dateStr)
if (match != null) {
println("День: ${match.groupValues[1]}") // 15
println("Месяц: ${match.groupValues[2]}") // 03
println("Год: ${match.groupValues[3]}") // 2025
// Альтернативный доступ через группы
println("День: ${match.groups[1]?.value}") // 15
}
Именованные группы
В Kotlin можно использовать именованные группы с синтаксисом (?<имя>...):
val namedRegex = "(?<day>\\d{2})-(?<month>\\d{2})-(?<year>\\d{4})".toRegex()
val namedMatch = namedRegex.find("31-12-2024")
if (namedMatch != null) {