Go Academy · 课时

性能技巧

重复使用已编译的正则表达式

第 4 / 4 课13 个步骤

性能技巧 是 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. regexp 基础
  2. 查找与提取
  3. 替换文本
  4. 性能技巧
← 返回 Go Academy