Skip to Content
Course content

76: Working with Regular Expressions in Kotlin

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I once spent an entire Tuesday afternoon writing a series of nested if statements and split() calls to parse a weirdly formatted log file from a legacy server. It was a nightmare. Every time I thought I'd handled an edge case—like a double space or a missing timestamp—another variation would break the logic. I eventually deleted the whole block of code and replaced it with a single regular expression. It felt like magic, but the real trick is knowing how to implement those patterns in Kotlin without making your code look like a cat walked across your keyboard.

Taming the Backslash with Raw Strings

If you've used regex in other languages, you know the "backslash plague"—where you have to escape every single backslash, leading to strings like "\\d{3}-\\d{3}". In Kotlin, we avoid this by using raw strings. By wrapping your pattern in triple quotes ("""), you can write your regex exactly as it should appear, which makes it significantly easier to read and maintain.

val orderIdRegex = """ORD-\d{5}-[A-Z]{3}""".toRegex()
val input = "Your order ORD-12345-XYZ has shipped!"

if (orderIdRegex.containsMatchIn(input)) {
    println("Found a valid order ID!")
}

Notice I used .toRegex(). While you can instantiate the Regex class directly, the extension function is the idiomatic way to go. I usually recommend defining your regexes as top-level constants or within a companion object if they're used frequently; compiling a regex pattern is relatively expensive, so you don't want to do it inside a loop.

Extracting Data via Destructuring

Checking if a string matches a pattern is fine, but usually, you actually want the data inside that pattern. This is where Kotlin's destructuring declarations really shine. By using capturing groups (parentheses in your regex), you can pull specific pieces of a string directly into variables.

val logPattern = """(\d{4}-\d{2}-\d{2}) \[(.*?)\] (.*)""".toRegex()
val logLine = "2023-10-12 [ERROR] Database connection failed"

val matchResult = logPattern.find(logLine)

if (matchResult != null) {
    val (date, level, message) = matchResult.destructured
    println("Date: $date, Level: $level, Message: $message")
}

The destructured property is a lifesaver here. Instead of manually calling groupValues[1], groupValues[2], and so on—which is a great way to accidentally trigger an IndexOutOfBoundsException—you can map the groups directly to named variables. It's cleaner and much more expressive.

Dynamic Replacements with Lambdas

Most developers know how to do a simple find-and-replace, but Kotlin allows you to pass a lambda to the replace function. This means you can evaluate the match and decide what the replacement should be on the fly. I use this all the time for data normalization, like converting different currency symbols to a standard ISO code.

val priceRegex = """([\$€£])(\d+)""".toRegex()
val text = "The item costs $10, but in Europe it is €12."

val normalized = priceRegex.replace(text) { matchResult ->
    val symbol = matchResult.groupValues[1]
    val amount = matchResult.groupValues[2]
    
    val currency = when (symbol) {
        "$" -> "USD"
        "€" -> "EUR"
        "£" -> "GBP"
        else -> "UNK"
    }
    "$currency $amount"
}

println(normalized) // The item costs USD 10, but in Europe it is EUR 12.

By using a lambda, you've turned a static search-and-replace into a small piece of logic. It's a powerful pattern that keeps your string manipulation logic contained and readable.




📋 Practical Task

Exercise: Building a Social Media Mention Extractor

You are building a simple notification system for a social media app. You need to extract all user mentions from a post. A mention is defined as the @ symbol followed by one or more alphanumeric characters (letters or numbers), but it cannot start with a number.

Your Task:

  • Create a Regex that identifies mentions (e.g., @kotlin_dev or @user123).
  • Write a function extractMentions(text: String): List<String> that finds all matches in a given string and returns them as a list without the @ symbol.
  • Test your function with the following string: "Hello @alice! Have you seen the latest update from @bob_builder and @charli3?"

Expected Output: [alice, bob_builder, charli3]

Rating
0 0

There are no comments for now.

to be the first to leave a comment.