性能技巧
重复使用已编译的正则表达式
性能技巧 是 CoddyKit 上的免费 Go Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Go Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Go Academy 课程共包含 4 节课。
编译需要成本
将模式字符串转换为可用的正则表达式需要执行一些操作。每次调用都这样做会浪费时间,尤其是在循环或热点路径中。
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile("[0-9]+")
fmt.Println(re.MatchString("x9"))
}只编译一次,经常复用
黄金法则是:将模式编译一次,然后复用 *regexp.Regexp。通常会将它放在包级变量中。
package main
import (
"fmt"
"regexp"
)
var numRe = regexp.MustCompile("[0-9]+")
func hasNumber(s string) bool {
return numRe.MatchString(s)
}
func main() {
fmt.Println(hasNumber("a1"))
fmt.Println(hasNumber("abc"))
}避免在循环中编译
不要在循环内部调用 MustCompile。请在循环之前完成编译,并在每次迭代中使用同一个值。
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile("[a-z]+")
inputs := []string{"go", "123", "rust"}
for _, s := range inputs {
fmt.Println(s, re.MatchString(s))
}
}正则表达式可安全共享
经过编译的 *regexp.Regexp 可由多个协程安全地并发使用。一个全局实例就可以供所有协程使用。
package main
import (
"fmt"
"regexp"
)
var re = regexp.MustCompile("go")
func main() {
fmt.Println(re.MatchString("golang"))
}有时 strings 更快
对于固定的字面子字符串,strings.Contains 比正则表达式更快。只有在确实需要模式匹配能力时,才应使用正则表达式。
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.Contains("hello world", "world"))
}优先使用带锚点的模式
使用 ^ 添加锚点后,如果开头不匹配,引擎就能快速失败,而不必扫描整个字符串。
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile("^https?://")
fmt.Println(re.MatchString("http://site"))
fmt.Println(re.MatchString("see http://x"))
}限制匹配数量
如果只需要少量匹配项,请向查找全部方法传入较小的 n,而不是传入 -1,以便尽早停止。
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile("[0-9]+")
fmt.Println(re.FindAllString("1 2 3 4 5", 2))
}注意回溯模式
Go 的 RE2 引擎可以避免灾难性回溯,但像 .* 这样过于宽泛的模式仍可能扫描超出需要的内容。请尽量明确指定匹配范围。
package main
import (
"fmt"
"regexp"
)
func main() {
broad := regexp.MustCompile(".*=.*")
precise := regexp.MustCompile("[a-z]+=[0-9]+")
fmt.Println(broad.MatchString("x=1"), precise.MatchString("x=1"))
}转义动态输入
如果模式中包含用户文本,请使用 regexp.QuoteMeta 对其进行转义,使特殊字符按字面字符处理。
package main
import (
"fmt"
"regexp"
)
func main() {
user := "a.b"
re := regexp.MustCompile(regexp.QuoteMeta(user))
fmt.Println(re.MatchString("a.b"))
fmt.Println(re.MatchString("axb"))
}缓存已编译的模式
如果模式是在运行时构建的,请将已编译的模式存储在映射中,并以模式字符串作为键,从而避免重复编译同一个模式。
package main
import (
"fmt"
"regexp"
)
var cache = map[string]*regexp.Regexp{}
func get(p string) *regexp.Regexp {
if re, ok := cache[p]; ok {
return re
}
re := regexp.MustCompile(p)
cache[p] = re
return re
}
func main() {
fmt.Println(get("[0-9]+").MatchString("7"))
}综合运用
经过良好调优的验证器只编译一次模式、为模式添加锚点,并在所有调用之间共享它。
package main
import (
"fmt"
"regexp"
)
var emailRe = regexp.MustCompile("^[a-z0-9]+@[a-z]+\\.[a-z]+$")
func main() {
fmt.Println(emailRe.MatchString("me@site.com"))
}快速检查
正则表达式性能方面最重要的一条建议是什么?
回顾:性能建议
您已经学会了如何高效使用正则表达式:
- 使用
MustCompile编译一次并复用;它支持并发安全。 - 对于固定子字符串,优先使用
strings;为模式添加锚点;限制匹配数量。 - 使用
QuoteMeta转义动态输入,并缓存运行时模式。
package main
import (
"fmt"
"regexp"
)
var re = regexp.MustCompile("^\\d{4}$")
func main() {
for _, s := range []string{"1234", "12"} {
fmt.Println(s, re.MatchString(s))
}
}用 AI 导师学习 Go — 免费
在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。
- 课程
- 51
- 课程
- 203
常见问题解答
「性能技巧」课时是免费的吗?
是的 — 「性能技巧」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Go Academy 课程的其余内容,请升级到 CoddyKit PRO。 Go Academy 课程共包含 4 节课。
「性能技巧」这节课中我会学到什么?
重复使用已编译的正则表达式 你通过在浏览器中直接运行的动手代码来练习 Go Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Go Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Go Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「性能技巧」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Go Academy 课中编写并运行代码吗?
能。每节 Go Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。