Регулярные выражения в Kotlin с примерами: на практике

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Регулярные выражения в Kotlin с примерами: на практике

Регулярные выражения (regex) — мощный инструмент для работы со строками. В Kotlin они реализованы на основе Java-классов Pattern и Matcher, но при этом язык добавляет собственные удобные расширения и синтаксический сахар. В этой статье мы разберем, как использовать регулярные выражения в Kotlin на реальных примерах: от простого поиска до замены и разбиения строк.

Основы синтаксиса: создание и компиляция

В Kotlin регулярное выражение можно создать двумя способами: через класс Regex или с помощью строки с шаблоном. Первый способ предпочтительнее, так как предоставляет больше методов.

// Способ 1: через класс Regex
val regex = Regex("\\d+") // одна или более цифр

// Способ 2: через строку (не рекомендуется для сложных выражений)
val pattern = "\\d+".toRegex()

Обратите внимание на двойной экранирующий слэш: \\d — это экранирование для Java/Kotlin, чтобы получить символ \d в строке. Для упрощения Kotlin поддерживает raw строки (тройные кавычки """), где экранирование не требуется:

val regex = Regex("""\d+""") // raw строка — обратные слэши не нужно дублировать

Также можно добавить флаги, например, RegexOption.IGNORE_CASE:

val regex = Regex("kotlin", RegexOption.IGNORE_CASE)

Поиск и проверка совпадений

Самые частые операции — проверить, содержит ли строка подстроку по шаблону, и найти все совпадения.

Проверка наличия совпадения: containsMatchIn

val text = "Мой номер: 123-45-678"
val digitRegex = Regex("""\d+""")

if (digitRegex.containsMatchIn(text)) {
    println("В тексте есть цифры!")
}

Поиск первого совпадения: find

val result = digitRegex.find(text)
println(result?.value) // Выведет: 123

Поиск всех совпадений: findAll

val allMatches = digitRegex.findAll(text)
allMatches.forEach { match ->
    println("Найдено: ${match.value}") // 123, 45, 678
}

Метод findAll возвращает последовательность объектов MatchResult. Каждый такой объект содержит не только само совпадение (.value), но и его позицию в строке (.range).

Работа с группами захвата

Группы захвата — это части шаблона, заключенные в круглые скобки. Они позволяют извлекать конкретные фрагменты данных. Например, разберем email на имя пользователя и домен:

val email = "user@example.com"
val emailRegex = Regex("""(.+)@(.+)""")
val matchResult = emailRegex.find(email)

if (matchResult != null) {
    val (username, domain) = matchResult.destructured
    println("Имя пользователя: $username") // user
    println("Домен: $domain")             // example.com
}

Конструкция destructured удобно разбивает группы на переменные. Если групп больше, можно обращаться по индексу:

val fullMatch = matchResult.value      // user@example.com
val group1 = matchResult.groupValues[1] // user
val group2 = matchResult.groupValues[2] // example.com

Именованные группы (Kotlin 1.7+)

Начиная с Kotlin 1.7, можно использовать именованные группы:

val namedRegex = Regex("""(?<name>\w+)@(?<domain>\w+\.\w+)""")
val match = namedRegex.find("contact@kotlin.org")
if (match != null) {
    println(match.groups["name"]?.value)   // contact
    println(match.groups["domain"]?.value) // kotlin.org
}

Замена текста с помощью replace

Метод replace позволяет заменить все совпадения на новую строку. В шаблоне замены можно ссылаться на группы захвата через $1, $2 и т.д.

val phone = "+7 (123) 456-78-90"
val cleanRegex = Regex("""[^\d+]""") // всё, кроме цифр и +
val cleaned = cleanRegex.replace(phone, "")
println(cleaned) // +71234567890

Пример замены с перестановкой групп (меняем формат даты):

val date = "2024-01-15"
val dateRegex = Regex("""(\d{4})-(\d{2})-(\d{2})""")
val newFormat = dateRegex.replace(date, "$3.$2.$1")
println(newFormat) // 15.01.2024

Для более сложной логики можно передать лямбду:

val text = "Цены: 100 руб, 250 руб, 30 руб"
val priceRegex = Regex("""\d+""")
val result = priceRegex.replace(text) { match ->
    val value = match.value.toInt()
    (value * 1.2).toInt().toString() // увеличиваем на 20%
}
println(result) // Цены: 120 руб, 300 руб, 36 руб

Разбиение строки: split

Метод split работает аналогично String.split, но принимает регулярное выражение:

val data = "apple,banana;cherry|date"
val splitRegex = Regex("""[,;|]""")
val fruits = splitRegex.split(data)
println(fruits) // [apple, banana, cherry, date]

Если нужно разбить только по первому совпадению, используйте splitToSequence с ограничением:

val limited = splitRegex.split(data, 2)
println(limited) // [apple, banana;cherry|date]

Практический пример: парсинг логов

Предположим, у нас есть строка лога сервера:

val log = "2024-03-15 14:30:00 ERROR: User 'alex' failed login from IP 192.168.1.1"
val logRegex = Regex(
    """(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) (INFO|ERROR|WARN): User '(\w+)' .*? IP (\                

Блоги

Рекомендаций по книгам