
在 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 兼容引擎)的通用规则,理解它能避免大量隐蔽的匹配错误。










