Go语言正则表达式完全指南

Go语言正则表达式完全指南

Go语言通过 regexp 包提供了强大的正则表达式支持。本指南将详细介绍Go中正则表达式的使用方法和最佳实践。

1. 基础知识

1.1 导入正则表达式包

import "regexp"

1.2 创建正则表达式

package main

import (
    "fmt"
    "regexp"
)

func main() {
    // 方式1:直接编译
    pattern := regexp.MustCompile(`\d+`)

    // 方式2:处理错误的编译方式
    pattern, err := regexp.Compile(`\d+`)
    if err != nil {
        panic(err)
    }
}

1.3 基本匹配方法

package main

import (
    "fmt"
    "regexp"
)

func main() {
    text := "Hello, my phone is 123-456-7890"
    pattern := regexp.MustCompile(`\d+`)

    // 1. FindString - 查找第一个匹配
    first := pattern.FindString(text)
    fmt.Println(first) // 输出: "123"

    // 2. FindAllString - 查找所有匹配
    all := pattern.FindAllString(text, -1)
    fmt.Println(all) // 输出: ["123", "456", "7890"]

    // 3. MatchString - 检查是否匹配
    isMatch := pattern.MatchString(text)
    fmt.Println(isMatch) // 输出: true

    // 4. FindStringIndex - 查找位置
    index := pattern.FindStringIndex(text)
    fmt.Println(index) // 输出起始和结束位置
}

2. 正则表达式语法

2.1 字符匹配

package main

import (
    "fmt"
    "regexp"
)

func main() {
    text := "Python3 is awesome! Price: $99.99"

    // 匹配单个字符
    digit := regexp.MustCompile(`\d`)
    fmt.Println(digit.FindAllString(text, -1))    // 匹配数字

    word := regexp.MustCompile(`\w`)
    fmt.Println(word.FindAllString(text, -1))     // 匹配字母/数字/下划线

    space := regexp.MustCompile(`\s`)
    fmt.Println(space.FindAllString(text, -1))    // 匹配空白字符

    // 自定义字符类
    vowels := regexp.MustCompile(`[aeiou]`)
    fmt.Println(vowels.FindAllString(text, -1))   // 匹配元音字母
}

2.2 数量词

package main

import (
    "fmt"
    "regexp"
)

func main() {
    text := "go golang gooo gooooo"

    // 不同数量词的使用
    pattern1 := regexp.MustCompile(`go*`)         // 匹配 g 后面跟着 0 个或多个 o
    pattern2 := regexp.MustCompile(`go+`)         // 匹配 g 后面跟着 1 个或多个 o
    pattern3 := regexp.MustCompile(`go?`)         // 匹配 g 后面跟着 0 个或 1 个 o
    pattern4 := regexp.MustCompile(`go{2}`)       // 匹配 g 后面跟着正好 2 个 o
    pattern5 := regexp.MustCompile(`go{2,4}`)     // 匹配 g 后面跟着 2 到 4 个 o

    fmt.Println(pattern1.FindAllString(text, -1))
    fmt.Println(pattern2.FindAllString(text, -1))
    fmt.Println(pattern3.FindAllString(text, -1))
    fmt.Println(pattern4.FindAllString(text, -1))
    fmt.Println(pattern5.FindAllString(text, -1))
}

3. 高级特性

3.1 分组和捕获

package main

import (
    "fmt"
    "regexp"
)

func main() {
    text := "John Smith, Jane Doe, Bob Johnson"

    // 基本分组
    namePattern := regexp.MustCompile(`(\w+)\s(\w+)`)
    
    // FindStringSubmatch 返回完整匹配和所有子匹配
    matches := namePattern.FindAllStringSubmatch(text, -1)
    for _, match := range matches {
        fmt.Printf("Full: %s, First: %s, Last: %s\n", match[0], match[1], match[2])
    }

    // 命名分组
    namedPattern := regexp.MustCompile(`(?P<first>\w+)\s(?P<last>\w+)`)
    for _, match := range namedPattern.FindAllStringSubmatch(text, -1) {
        fmt.Printf("Full: %s, First: %s, Last: %s\n", match[0], match[1], match[2])
    }
}

3.2 替换操作

package main

import (
    "fmt"
    "regexp"
)

func main() {
    text := "My phone numbers are 123-456-7890 and 098-765-4321"

    // 简单替换
    pattern := regexp.MustCompile(`\d{3}-\d{3}-\d{4}`)
    replaced := pattern.ReplaceAllString(text, "XXX-XXX-XXXX")
    fmt.Println(replaced)

    // 使用函数进行替换
    replacedFunc := pattern.ReplaceAllStringFunc(text, func(s string) string {
        return "CENSORED"
    })
    fmt.Println(replacedFunc)
}

4. 实用示例

4.1 数据验证

package main

import (
    "regexp"
)

// 验证电子邮件地址
func ValidateEmail(email string) bool {
    pattern := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
    return pattern.MatchString(email)
}

// 验证中国手机号
func ValidatePhone(phone string) bool {
    pattern := regexp.MustCompile(`^1[3-9]\d{9}$`)
    return pattern.MatchString(phone)
}

// 验证密码强度
func ValidatePassword(password string) bool {
    // 至少8位,包含大小写字母和数字
    pattern := regexp.MustCompile(`^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$`)
    return pattern.MatchString(password)
}

4.2 文本处理

package main

import (
    "regexp"
    "strings"
)

// 提取URL
func ExtractURLs(text string) []string {
    pattern := regexp.MustCompile(`https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+`)
    return pattern.FindAllString(text, -1)
}

// 清理文本(删除多余空白字符)
func CleanText(text string) string {
    pattern := regexp.MustCompile(`\s+`)
    return strings.TrimSpace(pattern.ReplaceAllString(text, " "))
}

// 提取日期
func ExtractDates(text string) []string {
    pattern := regexp.MustCompile(`\d{4}[-/]\d{1,2}[-/]\d{1,2}|\d{1,2}[-/]\d{1,2}[-/]\d{4}`)
    return pattern.FindAllString(text, -1)
}

5. 性能优化

5.1 预编译正则表达式

package main

import (
    "regexp"
    "sync"
)

// 使用包级变量存储编译后的正则表达式
var (
    emailRegex    = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
    phoneRegex    = regexp.MustCompile(`^1[3-9]\d{9}$`)
    passwordRegex = regexp.MustCompile(`^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$`)
)

// 在并发环境中安全使用正则表达式
type SafeRegexp struct {
    sync.RWMutex
    regex *regexp.Regexp
}

func (sr *SafeRegexp) MatchString(s string) bool {
    sr.RLock()
    defer sr.RUnlock()
    return sr.regex.MatchString(s)
}

5.2 优化建议

  1. 避免过度使用回溯
// 不好的写法 - 可能导致灾难性回溯
pattern := regexp.MustCompile(`.*foo.*`)

// 好的写法
pattern := regexp.MustCompile(`[^/]*foo[^/]*`)
  1. 使用适当的锚点
// 不好的写法
pattern := regexp.MustCompile(`\d+`)

// 好的写法 - 使用锚点限制匹配范围
pattern := regexp.MustCompile(`^\d+$`)

6. 常见问题和解决方案

6.1 处理多行文本

package main

import (
    "fmt"
    "regexp"
)

func main() {
    text := `First Line
    Second Line
    Third Line`

    // 使用(?m)标志启用多行模式
    pattern := regexp.MustCompile(`(?m)^.*Line`)
    matches := pattern.FindAllString(text, -1)
    fmt.Println(matches)
}

6.2 Unicode支持

package main

import (
    "fmt"
    "regexp"
)

func main() {
    text := "Hello 你好 こんにちは"

    // 匹配Unicode字符
    pattern := regexp.MustCompile(`\p{Han}+`)  // 匹配汉字
    matches := pattern.FindAllString(text, -1)
    fmt.Println(matches)  // 输出: [你好]
}

7. 测试正则表达式

package main

import (
    "regexp"
    "testing"
)

func TestEmailValidation(t *testing.T) {
    tests := []struct {
        email string
        want  bool
    }{
        {"test@example.com", true},
        {"invalid.email", false},
        {"user@domain.co.uk", true},
    }

    for _, tt := range tests {
        got := ValidateEmail(tt.email)
        if got != tt.want {
            t.Errorf("ValidateEmail(%q) = %v; want %v", tt.email, got, tt.want)
        }
    }
}

总结

Go语言的正则表达式实现具有以下特点:

  1. 性能优秀,支持预编译
  2. 语法简洁,API设计清晰
  3. 完整支持Unicode
  4. 线程安全

最佳实践:

  1. 总是使用反引号(`)来定义正则表达式字符串,避免过度转义
  2. 预编译频繁使用的正则表达式
  3. 在性能关键场景使用适当的锚点和限制
  4. 编写单元测试验证正则表达式的正确性
  5. 注意并发安全性

记住:在Go中使用正则表达式时,简单性和可维护性同样重要。避免编写过于复杂的表达式,适当拆分复杂的匹配逻辑可以提高代码的可维护性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老大白菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值