
在 go 中使用 `regexp.replaceallstring` 时,若字符类中连字符 `-` 位置不当(如 `[,;:]`),会被解释为 ASCII 范围符,意外包含 `0-9` 等字符,导致数字被误删。
正则表达式中的字符类(即方括号 [] 内的内容)支持范围表示法,例如 a-z 表示所有小写字母,0-9 表示所有 ASCII 数字。但当 – 出现在两个字符之间(非首尾)时,go 的正则引擎会将其视为范围连接符——这正是问题根源。
在原始代码中:
reg := regexp.MustCompile(`[[](){}"?!,-:;,']`)
子串 ,-: 实际被解析为从 ASCII 字符 ,(十进制 44)到 :(十进制 58)的连续范围,该范围完整覆盖了 0(48)、1(49)…9(57)——因此所有数字都被匹配并替换为空字符串,造成 “one 1 zer0” 变成 “one zer”。
✅ 正确做法:将 – 放在字符类的开头或结尾,此时它失去范围含义,仅作为字面量连字符匹配:
reg := regexp.MustCompile(`[[](){}"?!,:;,'-]`) // - 在末尾,安全 // 或等价写法: reg := regexp.MustCompile(`[-[](){}"?!,:;,'`]`) // - 在开头,同样安全
? 小技巧:若需同时匹配字面量 – 和其他符号,优先选末尾放置,语义更清晰;也可用反斜杠转义 -,但在 Go 的 regexp 包中,转义 – 并非必需且不推荐(官方文档明确指出:“To include a literal ‘-’, place it at the beginning or end of the character class”)。
⚠️ 注意事项:
- Go 的 regexp 包不支持 Unicode-aware 的 p{L} 类语法,处理国际化文本时需结合 unicode 包预处理;
- 若目标是“提取单词”(含数字、带符号词如 $100),建议改用 FindAllString 配合更精准模式,例如 [p{L}p{N}]+(?:[p{L}p{N}]+)*(需启用 (?U) 标志)或分步清洗+分割;
- 始终用 regexp.Compile(而非 MustCompile)并在开发期校验正则逻辑,避免静默错误。
总结:正则字符类中 – 的位置决定其语义——置于中间即为范围符,置于首尾即为字面量。这是 Go(及多数 POSIX 兼容引擎)的通用规则,理解它能避免大量隐蔽的匹配错误。