跳转到主内容
极星编程网:以代码为星,赴技术山海!

如何在 Go 中匹配任意重复字符(如 "cc"、"ff")

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

相关文章