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 优化建议
- 避免过度使用回溯
// 不好的写法 - 可能导致灾难性回溯
pattern := regexp.MustCompile(`.*foo.*`)
// 好的写法
pattern := regexp.MustCompile(`[^/]*foo[^/]*`)
- 使用适当的锚点
// 不好的写法
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语言的正则表达式实现具有以下特点:
- 性能优秀,支持预编译
- 语法简洁,API设计清晰
- 完整支持Unicode
- 线程安全
最佳实践:
- 总是使用反引号(`)来定义正则表达式字符串,避免过度转义
- 预编译频繁使用的正则表达式
- 在性能关键场景使用适当的锚点和限制
- 编写单元测试验证正则表达式的正确性
- 注意并发安全性
记住:在Go中使用正则表达式时,简单性和可维护性同样重要。避免编写过于复杂的表达式,适当拆分复杂的匹配逻辑可以提高代码的可维护性。