应使用 findallstring 而非 findstring 提取全部匹配子串,因后者仅返回首个匹配;findallstring 返回 []string,第二参数传 -1 表示不限数量;分组捕获需用 findallstringsubmatch,输入为 []byte,m[0] 为全匹配,m[1] 起为各分组。

提取字符串中所有匹配项用 FindAllString 而不是 FindString
如果你要拿回全部匹配的子串(比如从日志里抽多个 IP、从 HTML 里抓多个 href 值),FindString 只返回第一个,容易漏数据。必须用 FindAllString 或其变体。
常见错误是写成:re.FindString(text),结果只拿到头一个就停了。实际应:
re := regexp.MustCompile(`\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}`)
ips := re.FindAllString(text, -1) // -1 表示不限数量
-
FindAllString返回[]string,安全直接遍历 - 第2个参数传
-1是惯用法,别传0(那会只取前 0 个) - 如果文本超长且只想要前 5 个匹配,传
5即可,避免全量扫描
带分组捕获时优先用 FindAllStringSubmatch + bytes 处理
想提取括号里的内容(比如 URL 中的协议、域名、路径),FindAllString 拿不到分组;而 FindStringSubmatch 返回的是 [][]byte,需转 string 才能用——但别手动 string() 每个字节切片,容易出错。
更稳的做法是用 FindAllSubmatch 配合 regexp.FindAllStringSubmatchIndex 获取位置再切原字符串,或直接用 FindAllStringSubmatch(注意:它返回 [][]string,每个子匹配是 []string,首项是全匹配,后续是各分组):
re := regexp.MustCompile(`(https?)://([^/]+)(/.*)?`)
matches := re.FindAllStringSubmatch([]byte(text), -1)
for _, m := range matches {
if len(m) >= 3 {
scheme := string(m[1]) // 注意:m[0] 是全匹配,m[1] 是第一个括号
host := string(m[2])
path := string(m[3])
}
}
- 正则里用
()定义捕获组,序号从 1 开始,m[0]是整条匹配 - 输入必须是
[]byte,FindAllStringSubmatch不接受string - 如果某分组未匹配(比如
path为空),对应m[i]是nil,访问前务必检查len(m) > i
ReplaceAllStringFunc 适合简单替换,复杂逻辑得用 ReplaceAllStringSubmatchFunc
想把所有邮箱替换成 [EMAIL]?ReplaceAllStringFunc 简单直接。但若要根据分组内容动态替换(比如把 user@domain.com 替成 u***@d***.com),它没法访问分组,只能靠 ReplaceAllStringSubmatchFunc。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
后者传入函数接收的是完整匹配的 string,不是分组——所以还得在函数体内自己再跑一次正则提取,略绕。更推荐用 ReplaceAllFunc(Go 1.20+)配合 FindStringSubmatchIndex 手动拼接,或老老实实用 ReplaceAllStringFunc + 外层循环处理。
-
ReplaceAllStringFunc(re, text, func(s string) string { ... })的s是整个匹配串,不含上下文 - 若正则含分组但你只关心全匹配,它比
Submatch版本更轻量 - Go 1.20 前没有
ReplaceAllFunc,别误写成不存在的函数名
性能敏感场景下避免重复编译,用 regexp.MustCompile 预编译
在 HTTP handler 或循环里反复调用 regexp.Compile,会触发正则解析和状态机构建,开销远大于匹配本身。线上服务出现 CPU 飙高,十有八九是这儿没缓存。
正确做法是包级变量预编译:
var emailRe = regexp.MustCompile(`\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b`)
-
MustCompile在启动时报 panic,比运行时Compile返回 error 更早暴露问题 - 别在 struct 里放
*regexp.Regexp字段然后每次 new 时编译——它不是线程不安全的,但没必要重复做 - 如果正则模式来自配置或用户输入,才用
Compile并处理 error;否则一律MustCompile
分组索引、命名捕获、非贪婪匹配这些细节,都在编译阶段固化,运行时只管匹配。真正卡住的,往往是没意识到编译本身也有成本。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










