Go by Example

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