Regular Expressions
regexp.Compile, MustCompile, MatchString, FindString, FindAllString, ReplaceAllString, and named capture groups.
Go's regexp package uses the RE2 engine, which guarantees linear-time matching. Compile patterns once and reuse them; compiling per request is a common performance bug.
Compile a pattern with regexp.Compile (returns an error) or regexp.MustCompile (panics on invalid pattern - correct for package-level variables). Test a match with MatchString.
package main
import (
"fmt"
"regexp"
)
// Compile once at package level.
var emailRE = regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`)
func main() {
fmt.Println(emailRE.MatchString("user@example.com")) // true
fmt.Println(emailRE.MatchString("not-an-email")) // false
}FindString returns the first match. FindAllString returns all matches. Pass -1 as the count to get all matches; pass n to limit to the first n.
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`\d+`)
s := "I have 3 cats and 12 dogs"
fmt.Println(re.FindString(s)) // 3
fmt.Println(re.FindAllString(s, -1)) // [3 12]
fmt.Println(re.FindAllString(s, 1)) // [3]
}Replace matches with ReplaceAllString (literal replacement) or ReplaceAllStringFunc (replacement computed by a function).
package main
import (
"fmt"
"regexp"
"strings"
)
func main() {
re := regexp.MustCompile(`\b\w+\b`)
s := "hello world"
// Replace every word with its uppercase version.
result := re.ReplaceAllStringFunc(s, strings.ToUpper)
fmt.Println(result) // HELLO WORLD
// Simple literal replacement.
re2 := regexp.MustCompile(`\d+`)
fmt.Println(re2.ReplaceAllString(s, "[NUM]")) // hello world (no digits)
}Named capture groups let you extract labeled sub-matches by name. SubexpNames maps indices to names; FindStringSubmatch returns matched groups.
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})`)
match := re.FindStringSubmatch("Today is 2026-04-25")
names := re.SubexpNames()
result := map[string]string{}
for i, name := range names {
if i != 0 && name != "" {
result[name] = match[i]
}
}
fmt.Println(result) // map[day:25 month:04 year:2026]
}In production
Compile regexps once at package level with MustCompile - RE2 guarantees linear-time matching on input length, but compilation itself is expensive. Compiling inside a function called per request is a common performance bug that shows up clearly in profiling. Go's RE2 engine does not support lookaheads, lookbehinds, or backreferences - for patterns that require them, consider a purpose-built parser instead. For simple prefix/suffix checks, strings.HasPrefix and strings.HasSuffix are faster than a regexp match.
Enjoyed this? Get more essays on software craft delivered to your inbox.
Subscribe free