Go 标准库的 regexp 包基于 RE2 引擎,不支持反向引用(如 \1),因此无法直接用类似 JavaScript 的 /(.?)\1/g 模式匹配相邻重复字符;需改用循环遍历或引入兼容 PCRE 的第三方库。
go 标准库的 `regexp` 包基于 re2 引擎,不支持反向引用(如 `\1`),因此无法直接用类似 javascript 的 `/(.?)\1/g` 模式匹配相邻重复字符;需改用循环遍历或引入兼容 pcre 的第三方库。
在 JavaScript 或 Python 等语言中,匹配连续重复字符(如 "abccdeff" 中的 "cc" 和 "ff")非常直观:
但 Go 的 regexp 包("regexp")
明确不支持反向引用
——这是 RE2 的设计取舍,旨在保证正则匹配的时间复杂度为线性(O(n)),避免回溯导致的灾难性性能退化(如 ReDoS)。因此,以下写法在 Go 中
会编译失败或匹配为空
:
✅ 推荐方案一:使用 for 循环(简洁、高效、零依赖)
对字符串逐字符扫描,判断当前字符是否与前一字符相同,是最自然、最符合 Go 习惯的解法:
✅ 优势:时间复杂度 O(n),空间 O(1)(不含结果存储),逻辑清晰,无正则开销,且天然支持任意 Unicode 字符(若需更严谨的 rune 级处理,可稍作扩展)。
⚠️ 注意:上述代码按字节比较,适用于 ASCII 场景;若输入含多字节 UTF-8 字符(如中文、emoji),应改用 []rune 遍历以避免切片越界或错误拆分:
✅ 推荐方案二:使用 PCRE 兼容库(仅当必须用正则时)
若项目已重度依赖正则表达式逻辑,或需复用其他语言的正则规则,可引入支持反向引用的第三方库,例如
github.com/glenn-brown/golang-pkg-pcre
(封装 libpcre):
⚠️ 注意:该库需系统安装 libpcre,跨平台构建(如 CGO_ENABLED=0)受限,且引入 C 依赖会增加部署复杂度,
不推荐作为默认方案
。
总结
方案是否推荐适用场景循环遍历([]rune)✅ 强烈推荐大多数场景,尤其注重性能、可维护性与可移植性PCRE 第三方库⚠️ 谨慎选用已有大量 PCRE 正则需迁移,且可接受 CGO 依赖
Go 哲学强调“简单直接优于魔法”——面对 RE2 的限制,主动选择清晰的手动扫描,往往比绕路依赖外部正则引擎更稳健、更地道。
"abccdeff".match(/([a-z])\1/g) // → ["cc", "ff"]// ❌ 错误:Go regexp 不识别 \1,此模式非法
re := regexp.MustCompile(`(.)\1`)func findConsecutiveDuplicates(s string) []string {
var matches []string
for i := 1; i < len(s); i++ {
if s[i] == s[i-1] {
matches = append(matches, s[i-1:i+1])
}
}
return matches
}
// 使用示例
s := "abccdeff"
fmt.Println(findConsecutiveDuplicates(s)) // → [cc ff]func findConsecutiveDuplicatesRune(s string) []string {
r := []rune(s)
var matches []string
for i := 1; i < len(r); i++ {
if r[i] == r[i-1] {
matches = append(matches, string(r[i-1:i+1]))
}
}
return matches
}go get github.com/glenn-brown/golang-pkg-pcreimport pcre "github.com/glenn-brown/golang-pkg-pcre"
func withPCRE(s string) []string {
re, _ := pcre.Compile(`(.)\1`, 0)
matches := re.FindAllString(s, -1)
return matches // → [cc ff]
}