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 不支持回溯,保证性能
- 命名捕获: 复杂正则使用命名捕获组