A regular expression describes a pattern in text. It can recognize a stock code, search a message for numbers, or split a simple delimiter format. Kotlin's Regex wraps the target platform's pattern engine, so sophisticated features and edge cases can differ between JVM, JavaScript and other targets.
The strings lesson covers ordinary text operations. Keep using startsWith, split or a direct equality check when those explain the problem clearly. This guide uses Kotlin/JVM examples and teaches the important distinction between finding a matching substring and validating an entire input.
Write the rule before writing symbols
Suppose a code must contain two uppercase ASCII letters, a hyphen, and three digits. In a pattern, [A-Z] selects one permitted letter, {2} repeats that selection twice, and [0-9]{3} selects three ASCII digits. A hyphen outside a character class is an ordinary literal here.
Regex.matches and matchEntire require the whole input to fit. Find searches for a matching part anywhere in the input, and containsMatchIn answers only whether any such match exists. Choosing the wrong operation can accept extra text around an otherwise valid code. Whole-input validation should not depend on accidentally choosing a searching function.
Triple-quoted Kotlin strings reduce backslash escaping for patterns. A regular quoted string requires escaping a backslash that should reach the pattern engine. Raw strings still support Kotlin dollar interpolation, so they do not mean every character bypasses Kotlin's syntax. Keep patterns small enough that both language layers are understandable.
Captures, optional pieces and safe conversion
Parentheses capture selected portions of a match. Access them through groupValues or groups. An optional unmatched group needs careful handling: the groups collection can represent an absent group as null, while groupValues provides an empty string at that position. Decide whether empty and missing mean the same thing before building a model.
Convert captured numeric text with toIntOrNull when invalid or oversized values should produce a normal rejection. A pattern containing many digits does not guarantee that the resulting number fits an Int. A date-shaped match does not guarantee that a real calendar date exists either; hand that part to a date parser.
Keep the compiled Regex in a reusable property when the same stable rule is applied repeatedly. Creating it inside every iteration makes the pattern compilation repeat unnecessarily. For a dynamic rule, validate the pattern construction separately from matching the input, because malformed regular-expression syntax can throw before any text is inspected.
Make the character policy explicit too. This tutorial uses [0-9] and [A-Z] because the stock grammar deliberately accepts ASCII codes. A human-name form has different Unicode requirements and should not inherit that restriction accidentally. Decide what the domain permits before selecting a convenient character class.
Literal data is different from pattern syntax
If a user searches for a literal product label such as A+B, the plus sign has pattern meaning. Regex.escape turns literal text into a safe literal portion of a pattern. Do not interpolate untrusted text directly into a regex unless you explicitly intend to let the user define pattern syntax.
Replacement strings can also have their own interpretation for capture references. Prefer a transform lambda when the replacement is computed data and should not be interpreted as replacement syntax. Escaping a pattern and escaping a replacement are different operations; neither replaces validation of the application action that follows.
Complex nested repetition can lead to excessive matching work on adversarial input. Keep request sizes bounded and choose simple patterns. Regex is unsuitable for reliably parsing arbitrary nested documents or rich language grammars. Use a real parser for JSON, HTML or SQL rather than gradually expanding a fragile collection of textual guesses.
Official references and a review strategy
Read the Kotlin Regex API for matching and platform behavior, and the JVM Pattern reference for engine syntax. Test valid examples alongside near misses: extra prefixes, empty captures, oversized numbers, unexpected whitespace and literal punctuation. A rule is not finished until its rejection cases match your intended grammar.
Kotlin Regex Matching and Parsing Examples
Each example shows the code, the output it produces and what each part does.
Parse one complete stock record
Group zero is the complete match; groups one and two correspond to the parentheses. Trimming is a deliberate policy allowing surrounding whitespace. The pattern checks shape, then the numeric range checks business meaning. A syntactically valid quantity can still be unacceptable.
data class StockRecord(val code: String, val quantity: Int)
private val stockPattern = Regex("""([A-Z]{2}-[0-9]{3}):([0-9]{1,3})""")
fun parseStock(text: String): StockRecord? {
val match = stockPattern.matchEntire(text.trim()) ?: return null
val quantity = match.groupValues[2].toIntOrNull() ?: return null
if (quantity !in 1..100) return null
return StockRecord(match.groupValues[1], quantity)
}
fun main() {
println(parseStock(" AB-042:12 "))
println(parseStock("prefix AB-042:12"))
println(parseStock("AB-042:999"))
}StockRecord(code=AB-042, quantity=12)
null
nullKotlin Regex Matching and Parsing in Real Projects
Worked exercise: Extract bounded order numbers
A status message can contain references written as ORD followed by exactly three ASCII digits. Extract their numeric portions in encounter order. Reject a reference that continues with another digit, so ORD1234 is not mistaken for ORD123. The negative lookahead used here is supported by the JVM pattern engine.
private val orderPattern = Regex("""ORD([0-9]{3})(?![0-9])""")
fun orderNumbers(message: String): List<Int> = orderPattern.findAll(message)
.mapNotNull { it.groupValues[1].toIntOrNull() }
.toList()
fun main() {
println(orderNumbers("Sent ORD007, then ORD042; skip ORD1234"))
println(orderNumbers("No orders today"))
}[7, 42]
[]This is a searching task, so findAll is appropriate. A leading zero disappears during conversion because the result is a number, not a formatted identifier. Preserve the captured string instead if formatting is meaningful. Add a preceding boundary rule if your business grammar must reject embedded forms such as XORD007; the stated exercise only restricts the following digits.
Kotlin Regex Matching and Parsing FAQ
Should I anchor every pattern with caret and dollar?
Not automatically. When matchEntire expresses whole-input validation, its API contract already requires the entire input. Find and findAll deliberately search within larger text. Anchors can also interact with multiline options and engine rules, so blindly adding them can hide the operation you really intended. Choose the matching API first, then add anchors only when their line or input boundary meaning is part of the documented grammar.
Why does a number match but fail conversion?
A pattern checks text shape, whereas conversion checks whether that text represents a value within the chosen numeric type's range. A hundred digits can all satisfy a digit pattern and still exceed Int capacity. Use a bounded grammar and toIntOrNull, then apply the business range separately. If the domain requires larger values, select Long or an appropriate big-number type deliberately rather than silently discarding overflow.
Can the same Regex behave differently on Kotlin targets?
Yes. Kotlin provides a common abstraction, but its documentation explicitly notes pattern and option differences by platform. JVM Regex uses the Java pattern engine, while JavaScript follows its own regular-expression facilities. These examples target Kotlin/JVM. For shared multiplatform code, use the intersection of supported features and run tests on every target you ship, particularly for lookarounds, Unicode classes, group behavior and replacement syntax.
Is a regex enough to validate an email or date?
Usually it is only one layer. A practical email form may use a modest shape check and then confirm delivery or ownership through its workflow. A date requires calendar validation beyond separators and digit counts. Avoid claiming that a large pattern proves every domain rule. Separate text recognition, conversion and business checks so each failure can produce a useful message and the rules can be tested independently.
Kotlin Regex Matching and Parsing Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
A captured digit string must represent an Int from 1 through 100. Which approach handles both limits?
Kotlin Regex Matching and Parsing Quiz
2 questions to check what you learned.
Which API is best for requiring the whole input to fit a pattern?
- containsMatchIn
- findAll
- matchEntire
Why apply Regex.escape to a user-entered literal search term?
- Its punctuation should be treated as text rather than pattern operators.
- It encrypts the term.
- It converts the term into an integer.