Regular Expressions
Prerequisites: Strings and GStrings
Groovy has three regex operators on top of Java's java.util.regex:
| Operator | Result |
|---|---|
~/pattern/ |
a compiled Pattern |
text =~ /pattern/ |
a Matcher (truthy if the pattern is found) |
text ==~ /pattern/ |
true if the whole text matches |
def phone = /\d{3}-\d{4}/
println '555-1234' ==~ phone
println 'call 555-1234 now' ==~ phone
if ('call 555-1234 now' =~ phone) println 'found a phone number'
println((~/[aeiou]/).class.simpleName)
true
false
found a phone number
Pattern
Test your patterns interactively with the Regex Tester.
Extracting groups
def log = '2026-09-29 ERROR [db] connection lost'
def m = log =~ /(\d{4})-(\d\d)-(\d\d) (\w+) \[(\w+)\]/
if (m) {
def (all, year, month, day, level, source) = m[0]
println "$level from $source on $day/$month/$year"
}
def named = log =~ /(?<level>[A-Z]+) \[(?<src>\w+)\]/
named.find()
println named.group('level') + ' / ' + named.group('src')
ERROR from db on 29/09/2026
ERROR / db
m[0] is the first match as a list: the whole match followed by each
group. Named groups ((?<name>...)) are read with group('name').
Finding all matches
def text = 'Order #123 shipped, #456 pending, #789 cancelled'
println text.findAll(/#\d+/)
println text.findAll(/#(\d+) (\w+)/) { all, id, status -> "$id:$status" }
println((text =~ /#\d+/).size())
text.eachMatch(/#(\d+)/) { all, id -> print "${id.toInteger() * 2} " }
println()
[#123, #456, #789]
[123:shipped, 456:pending, 789:cancelled]
3
246 912 1578
Replacing
def s = 'hello world from groovy'
println s.replaceAll(/o/, '0')
println s.replaceAll(/\b(\w)/) { all, first -> first.toUpperCase() }
println '2026-09-29'.replaceFirst(/(\d+)-(\d+)-(\d+)/, '$3.$2.$1')
println 'a1b22c333'.split(/\d+/)
hell0 w0rld fr0m gr00vy
Hello World From Groovy
29.09.2026
[a, b, c]
replaceAll with a closure computes each replacement; in a plain
replacement string, $1 refers to a group.
Common mistakes
- Using
=~when you mean a full match:'abc123' =~ /\d+/is truthy because it finds digits. Use==~for validation. - Writing regexes in double-quoted strings:
"\d"is an invalid escape. Use slashy strings (/\d/).
Practice
Extract all email addresses from
'Contact ada@example.com or ops@test.org today' and print their
domains.
Show solution
def text = 'Contact ada@example.com or ops@test.org today'
def emails = text.findAll(/[\w.]+@[\w.]+\w/)
println emails
println emails.collect { it.split('@')[1] }
[ada@example.com, ops@test.org]
[example.com, test.org]