1. go
  2. /standard library
  3. /regex

Working with Regular Expressions in Go

Go's regexp package provides comprehensive support for regular expressions. This guide covers pattern matching, text processing, and common regex patterns.

Basic Regular Expressions

Creating Regex Patterns

func regexBasics() error {
    // Compile regex pattern
    pattern := `^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`
    re, err := regexp.Compile(pattern)
    if err != nil {
        return fmt.Errorf("compiling regex: %w", err)
    }
    
    // MustCompile panics on error
    emailRe := regexp.MustCompile(pattern)
    
    // Case-insensitive compilation
    caseInsensitive := regexp.MustCompile(`(?i)hello`)
    
    return nil
}

Pattern Matching

func matchExamples() {
    re := regexp.MustCompile(`\d{3}-\d{2}-\d{4}`)
    
    // Test if string matches pattern
    matches := re.MatchString("123-45-6789")
    
    // Find first match
    match := re.FindString("SSN: 123-45-6789")
    
    // Find all matches
    allMatches := re.FindAllString("SSN1: 123-45-6789, SSN2: 987-65-4321", -1)
    
    // Find match locations
    loc := re.FindStringIndex("SSN: 123-45-6789")
    if loc != nil {
        start, end := loc[0], loc[1]
        fmt.Printf("Match from %d to %d\n", start, end)
    }
}

Advanced Features

Capturing Groups

func groupExamples() {
    re := regexp.MustCompile(`(\w+):(\d+)`)
    
    // Find submatch
    match := re.FindStringSubmatch("port:8080")
    if len(match) > 0 {
        full := match[0]  // "port:8080"
        key := match[1]   // "port"
        value := match[2] // "8080"
    }
    
    // Find all submatches
    text := "port:8080 host:localhost"
    allMatches := re.FindAllStringSubmatch(text, -1)
    for _, match := range allMatches {
        fmt.Printf("Key: %s, Value: %s\n", match[1], match[2])
    }
}

Named Groups

func namedGroups() {
    re := regexp.MustCompile(`(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})`)
    
    match := re.FindStringSubmatch("2024-03-19")
    if len(match) > 0 {
        // Get group names
        names := re.SubexpNames()
        
        // Create map of named groups
        result := make(map[string]string)
        for i, name := range names {
            if i != 0 && name != "" {
                result[name] = match[i]
            }
        }
        
        fmt.Printf("Year: %s, Month: %s, Day: %s\n",
            result["year"], result["month"], result["day"])
    }
}

String Manipulation

Replacement

func replaceExamples() {
    re := regexp.MustCompile(`\b\w+@\w+\.\w+\b`)
    
    // Replace first match
    result := re.ReplaceString(
        "Contact: [email protected]",
        "[EMAIL REDACTED]",
    )
    
    // Replace all matches
    result = re.ReplaceAllString(
        "Email1: [email protected], Email2: [email protected]",
        "[EMAIL REDACTED]",
    )
    
    // Replace with function
    result = re.ReplaceAllStringFunc(
        "[email protected] [email protected]",
        func(s string) string {
            return strings.Repeat("*", len(s))
        },
    )
}

Splitting

func splitExamples() {
    re := regexp.MustCompile(`[,\s]+`)
    
    // Split string
    parts := re.Split("apple,orange banana  grape", -1)
    // ["apple", "orange", "banana", "grape"]
    
    // Split with limit
    parts = re.Split("apple,orange banana  grape", 2)
    // ["apple", "orange banana  grape"]
}

Common Patterns

Validation Patterns

var (
    emailPattern    = `^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`
    phonePattern    = `^\+?1?\d{9,15}$`
    usernamePattern = `^[a-zA-Z0-9_]{3,20}$`
    passwordPattern = `^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$`
    urlPattern     = `^https?://[\w\-]+(\.[\w\-]+)+[/#?]?.*$`
)

func validatePatterns() {
    validators := map[string]*regexp.Regexp{
        "email":    regexp.MustCompile(emailPattern),
        "phone":    regexp.MustCompile(phonePattern),
        "username": regexp.MustCompile(usernamePattern),
        "password": regexp.MustCompile(passwordPattern),
        "url":      regexp.MustCompile(urlPattern),
    }
    
    // Validate input
    input := map[string]string{
        "email":    "[email protected]",
        "phone":    "+1234567890",
        "username": "john_doe",
        "password": "Password123",
        "url":      "https://example.com",
    }
    
    for field, value := range input {
        if !validators[field].MatchString(value) {
            fmt.Printf("Invalid %s: %s\n", field, value)
        }
    }
}

Performance Tips

Compile Once, Use Many Times

var (
    // Compile patterns at init
    emailRe = regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`)
)

func validateEmail(email string) bool {
    return emailRe.MatchString(email)
}

Optimize Patterns

// Good - Anchored pattern
goodRe := regexp.MustCompile(`^prefix`)

// Bad - Unanchored pattern
badRe := regexp.MustCompile(`prefix`)

// Good - Non-capturing group
goodRe = regexp.MustCompile(`(?:prefix)`)

// Bad - Capturing group when not needed
badRe = regexp.MustCompile(`(prefix)`)

Best Practices

  1. Error Handling

    func compilePattern(pattern string) (*regexp.Regexp, error) {
        re, err := regexp.Compile(pattern)
        if err != nil {
            return nil, fmt.Errorf("invalid pattern %q: %w", pattern, err)
        }
        return re, nil
    }
    
  2. Input Validation

    func validateInput(input string, maxLen int) error {
        if len(input) > maxLen {
            return fmt.Errorf("input too long: max %d chars", maxLen)
        }
        return nil
    }
    
  3. Pattern Documentation

    // UserPattern matches usernames:
    // - Must start with a letter
    // - Can contain letters, numbers, and underscores
    // - Length between 3 and 20 characters
    const UserPattern = `^[a-zA-Z][a-zA-Z0-9_]{2,19}$`
    

Next Steps