← 标准库概览 | sync 包 →

regexp 正则表达式 - 文本处理

regexp 包提供了正则表达式操作功能,支持 RE2 语法。掌握正则表达式是进行文本匹配、提取、替换等操作的基础。

📌 核心概念

🔍

匹配

Match 系列函数

MatchString
📋

查找

Find 系列函数

FindAllString
🔄

替换

ReplaceAll 系列

ReplaceAllString
✂️

分割

Split 函数

Split

快速开始

📝 基础匹配

package main

import (
    "fmt"
    "regexp"
)

func main() {
    // 方式 1: 直接匹配 (简单场景)
    matched, _ := regexp.MatchString(`^\d{3}-\d{4}$`, "123-4567")
    fmt.Println(matched) // true
    
    // 方式 2: 编译正则 (推荐,可复用)
    re := regexp.MustCompile(`[a-z]+`)
    
    // 是否匹配
    fmt.Println(re.MatchString("hello123")) // true
    
    // 查找第一个匹配
    fmt.Println(re.FindString("hello123world")) // hello
    
    // 查找所有匹配
    fmt.Println(re.FindAllString("hello123world456", -1))
    // [hello world]
    
    // 查找匹配位置
    loc := re.FindStringIndex("hello123")
    fmt.Println(loc) // [0 5]
}

💡 regexp 要点

  • RE2 语法: 不支持回溯,保证线性时间复杂度
  • MustCompile: 编译失败会 panic,适合硬编码正则
  • Compile: 返回 error,适合用户输入的正则
  • 复用: 正则应编译一次,重复使用

查找函数详解

📝 Find 系列函数

package main

import (
    "fmt"
    "regexp"
)

func main() {
    re := regexp.MustCompile(`(\w+):(\d+)`)
    text := "age:25 score:90 grade:A"
    
    // FindString: 第一个匹配的字符串
    fmt.Println(re.FindString(text)) // age:25
    
    // FindAllString: 所有匹配
    fmt.Println(re.FindAllString(text, -1)) // [age:25 score:90]
    fmt.Println(re.FindAllString(text, 1)) // [age:25] 限制数量
    
    // FindStringSubmatch: 带捕获组
    submatch := re.FindStringSubmatch(text)
    fmt.Println(submatch)
    // [age:25 age 25] - [完整匹配 组 1 组 2]
    
    // FindAllStringSubmatch: 所有带捕获组
    allSubmatch := re.FindAllStringSubmatch(text, -1)
    for _, m := range allSubmatch {
        fmt.Printf("Full: %s, Key: %s, Value: %s\n", m[0], m[1], m[2])
    }
    
    // FindStringIndex: 匹配位置
    loc := re.FindStringIndex(text)
    fmt.Println(loc) // [0 7]
}

替换函数

📝 ReplaceAll 系列

package main

import (
    "fmt"
    "regexp"
)

func main() {
    re := regexp.MustCompile(`\d+`)
    
    // ReplaceAllString: 字符串替换
    result := re.ReplaceAllString("abc123def456", "X")
    fmt.Println(result) // abcXdefX
    
    // 使用捕获组引用
    re2 := regexp.MustCompile(`(\w+)\s+(\w+)`)
    result2 := re2.ReplaceAllString("John Doe", "$2, $1")
    fmt.Println(result2) // Doe, John
    
    // ReplaceAllStringFunc: 函数替换
    re3 := regexp.MustCompile(`\w+`)
    result3 := re3.ReplaceAllStringFunc("hello world", func(s string) string {
        return "[" + s + "]"
    })
    fmt.Println(result3) // [hello] [world]
    
    // 隐藏敏感信息
    re4 := regexp.MustCompile(`\d{4}`)
    phone := "13800138000"
    masked := re4.ReplaceAllStringFunc(phone, func(s string) string {
        return "****"
    })
    fmt.Println(masked) // 138****8000
}

分割函数

📝 Split 函数

package main

import (
    "fmt"
    "regexp"
    "strings"
)

func main() {
    // 正则分割
    re := regexp.MustCompile(`[\s,]+`)
    parts := re.Split("apple,banana orange grape", -1)
    fmt.Println(parts)
    // [apple banana orange grape]
    
    // 限制分割数量
    parts2 := re.Split("a,b c d", 2)
    fmt.Println(parts2) // [a b c d]
    
    // 对比 strings.Split
    parts3 := strings.Split("a,b,c", ",")
    fmt.Println(parts3) // [a b c]
}

命名捕获组

📝 命名捕获组使用

package main

import (
    "fmt"
    "regexp"
)

func main() {
    // 命名捕获组语法:(?P<name>pattern)
    re := regexp.MustCompile(`(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})`)
    
    text := "Date: 2024-03-15"
    match := re.FindStringSubmatch(text)
    
    // 获取组名
    names := re.SubexpNames()
    fmt.Println(names) // [ year month day]
    
    // 构建命名映射
    for i, name := range names {
        if i != 0 && name != "" {
            fmt.Printf("%s: %s\n", name, match[i])
        }
    }
    // year: 2024
    // month: 03
    // day: 15
    
    // 替换时使用命名组
    result := re.ReplaceAllString(text, "$month/$day/$year")
    fmt.Println(result) // 03/15/2024
}

常用正则模式

📖 实用正则表达式

var (
    // 邮箱
    emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
    
    // 手机号 (中国)
    phoneRegex = regexp.MustCompile(`^1[3-9]\d{9}$`)
    
    // 身份证号
    idCardRegex = regexp.MustCompile(`^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$`)
    
    // URL
    urlRegex = regexp.MustCompile(`^https?://[^\s/$.?#].[^\s]*$`)
    
    // IP 地址
    ipRegex = regexp.MustCompile(`^(\d{1,3}\.){3}\d{1,3}$`)
    
    // 中文
    chineseRegex = regexp.MustCompile(`^[\u4e00-\u9fa5]+$`)
    
    // 密码强度 (至少 8 位,包含字母和数字)
    passwordRegex = regexp.MustCompile(`^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$`)
    
    // 日期 YYYY-MM-DD
    dateRegex = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`)
)

func ValidateEmail(email string) bool {
    return emailRegex.MatchString(email)
}

func ValidatePhone(phone string) bool {
    return phoneRegex.MatchString(phone)
}

性能优化

📊 正则性能建议

package main

import (
    "regexp"
    "sync"
)

// ✅ 推荐:全局编译,复用正则
var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)

func IsValidEmail(email string) bool {
    return emailRegex.MatchString(email)
}

// ✅ 推荐:使用 sync.Once 延迟初始化
var (
    phoneRegex *regexp.Regexp
    once       sync.Once
)

func getPhoneRegex() *regexp.Regexp {
    once.Do(func() {
        phoneRegex = regexp.MustCompile(`^1[3-9]\d{9}$`)
    })
    return phoneRegex
}

// ❌ 不推荐:每次调用都编译
func BadValidate(email string) bool {
    re, _ := regexp.Compile(`^email@regex$`)
    return re.MatchString(email)
}

最佳实践

✅ regexp 使用建议

  • 预编译: 全局变量存储编译后的正则
  • MustCompile: 硬编码正则使用 MustCompile
  • 简单匹配: 简单场景使用 MatchString
  • 避免回溯: RE2 不支持回溯,保证性能
  • 命名捕获: 复杂正则使用命名捕获组