Go 语言正则表达式
regexp 的编译、匹配、分组、替换,以及 RE2 的性能特性。
Key PointsGo 用 RE2 引擎,保证线性时间——没有回溯,所以不支持反向引用和环视。
编译与匹配
Go
// 编译一次,重复使用(推荐)
re := regexp.MustCompile(`\d{3}-\d{4}`)
fmt.Println(re.MatchString("电话 555-1234"))
fmt.Println(re.FindString("电话 555-1234"))
// 一次性使用
ok, _ := regexp.MatchString(`^a`, "abc")
fmt.Println(ok)Output
true
555-1234
trueMustCompile panics on a bad pattern — fine for package-level vars (a broken regex is a programmer error, fail fast). For runtime-built patterns use regexp.Compile and handle the error.
查找
Go
re := regexp.MustCompile(`(\w+)@(\w+)\.com`)
s := "联系 tom@example.com 或 jerry@test.com"
fmt.Println(re.FindString(s)) // 第一个整体匹配
fmt.Println(re.FindAllString(s, -1)) // 全部,-1 表示不限
// 分组:FindStringSubmatch
m := re.FindStringSubmatch(s)
fmt.Println(m) // [整体 组1 组2]
// 全部匹配的分组
for _, g := range re.FindAllStringSubmatch(s, -1) {
fmt.Println(g[1], g[2])
}Output
tom@example.com
[tom@example.com jerry@test.com]
[tom@example.com tom example]
tom example
jerry test| Method | Returns |
|---|---|
MatchString | bool |
FindString | 第一个匹配的字符串 |
FindStringIndex | 起止下标 [2]int |
FindStringSubmatch | 含分组的 []string |
FindAllString | 全部匹配 |
ReplaceAllString | 替换后的字符串 |
替换
Go
re := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`)
// $1 $2 引用分组
fmt.Println(re.ReplaceAllString("2026-10-31", "$3/$2/$1"))
// 用函数替换
out := re.ReplaceAllStringFunc("2026-10-31 与 2025-01-01", func(s string) string {
return "[" + s + "]"
})
fmt.Println(out)
// 转义用户输入
fmt.Println(regexp.QuoteMeta("1+1=2"))Output
31/10/2026
[2026-10-31] 与 [2025-01-01]
1\+1=2When interpolating user input into a pattern, always regexp.QuoteMeta — otherwise characters like (, * and + break or invalidate the pattern.
RE2 的限制
| Feature | Supported | Workaround |
|---|---|---|
反向引用 \1 | 不支持 | 用代码二次校验 |
环视 (?=) (?<=) | 不支持 | 改写正则或用两次匹配 |
原子组 (?>...) | 不支持 | 不需要,RE2 无回溯 |
命名分组 (?P<x>) | 支持 | — |
非贪婪 *? | 支持 | — |
RE2's big win is no ReDoS: matching is O(n·m) for any input. The cost is no backreferences or lookaround — many PCRE patterns must be rewritten.
命名分组
Go
re := regexp.MustCompile(`(?P<year>\d{4})-(?P<month>\d{2})`)
m := re.FindStringSubmatch("2026-10")
for _, name := range re.SubexpNames() {
if name != "" {
fmt.Println(name, "=", m[re.SubexpIndex(name)])
}
}Output
year = 2026
month = 10Regexes get unreadable fast — comment them or use
(?x) verbose mode. For HTML, JSON or CSV, use a real parser, never a regex.编译与匹配
Go
// 编译一次,重复使用(推荐)
re := regexp.MustCompile(`\d{3}-\d{4}`)
fmt.Println(re.MatchString("电话 555-1234"))
fmt.Println(re.FindString("电话 555-1234"))
// 一次性使用
ok, _ := regexp.MatchString(`^a`, "abc")
fmt.Println(ok)Output
true
555-1234
trueMustCompile 在正则非法时 panic,适合包级变量(正则写错是程序员的错,早崩早发现)。运行时拼出来的正则用 regexp.Compile 并处理 error。
查找
Go
re := regexp.MustCompile(`(\w+)@(\w+)\.com`)
s := "联系 tom@example.com 或 jerry@test.com"
fmt.Println(re.FindString(s)) // 第一个整体匹配
fmt.Println(re.FindAllString(s, -1)) // 全部,-1 表示不限
// 分组:FindStringSubmatch
m := re.FindStringSubmatch(s)
fmt.Println(m) // [整体 组1 组2]
// 全部匹配的分组
for _, g := range re.FindAllStringSubmatch(s, -1) {
fmt.Println(g[1], g[2])
}Output
tom@example.com
[tom@example.com jerry@test.com]
[tom@example.com tom example]
tom example
jerry test| 方法 | 返回 |
|---|---|
MatchString | bool |
FindString | 第一个匹配的字符串 |
FindStringIndex | 起止下标 [2]int |
FindStringSubmatch | 含分组的 []string |
FindAllString | 全部匹配 |
ReplaceAllString | 替换后的字符串 |
替换
Go
re := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`)
// $1 $2 引用分组
fmt.Println(re.ReplaceAllString("2026-10-31", "$3/$2/$1"))
// 用函数替换
out := re.ReplaceAllStringFunc("2026-10-31 与 2025-01-01", func(s string) string {
return "[" + s + "]"
})
fmt.Println(out)
// 转义用户输入
fmt.Println(regexp.QuoteMeta("1+1=2"))Output
31/10/2026
[2026-10-31] 与 [2025-01-01]
1\+1=2把用户输入拼进正则时必须 regexp.QuoteMeta,否则 ( * + 这些字符会让正则失效甚至编译失败。
RE2 的限制
| 特性 | 支持 | 替代 |
|---|---|---|
反向引用 \1 | 不支持 | 用代码二次校验 |
环视 (?=) (?<=) | 不支持 | 改写正则或用两次匹配 |
原子组 (?>...) | 不支持 | 不需要,RE2 无回溯 |
命名分组 (?P<x>) | 支持 | — |
非贪婪 *? | 支持 | — |
RE2 的最大好处是不会 ReDoS:任何输入下匹配时间都是 O(n·m)。代价就是不支持反向引用和环视——很多从 PCRE 迁过来的正则要重写。
命名分组
Go
re := regexp.MustCompile(`(?P<year>\d{4})-(?P<month>\d{2})`)
m := re.FindStringSubmatch("2026-10")
for _, name := range re.SubexpNames() {
if name != "" {
fmt.Println(name, "=", m[re.SubexpIndex(name)])
}
}Output
year = 2026
month = 10正则可读性差,超过一行就该加注释或用
(?x) 展开模式。复杂解析(HTML、JSON、CSV)不要用正则——用对应的解析器。