
Go 的 regexp 包中 \w 仅匹配 ASCII 字母数字和下划线,不支持 Cyrillic、Czech 等 Unicode 字母;需改用 Unicode 类 \p{L} 并显式补充 \d 和 _,才能正确提取多语言单词。
go 的 `regexp` 包中 `\w` 仅匹配 ascii 字母数字和下划线,不支持 cyrillic、czech 等 unicode 字母;需改用 unicode 类 `\p{l}` 并显式补充 `\d` 和 `_`,才能正确提取多语言单词。
在 Go 中处理国际化文本时,正则表达式 \w+ 常被误认为能匹配“所有语言的单词”,但事实并非如此。根据 Go 正则语法文档,\w 是严格等价于 [0-9A-Za-z_] 的 ASCII-only 缩写,完全忽略 Unicode 字母(如俄语的 Раз、捷克语的 tři)。这导致 FindAllString 对含西里尔或带重音字符的文本返回空切片或错误切分(如将 tři 拆为 t i ty i)。
解决方法是使用 Unicode 字符类 \p{L}(表示任意 Unicode 字母),并手动补全数字 \d 和下划线 _,构成完整单词字符集:
package main
import (
"fmt"
"regexp"
)
func getWordsFrom(text string) []string {
// ✅ 正确:支持所有 Unicode 字母 + 数字 + 下划线
re := regexp.MustCompile(`[\p{L}\d_]+`)
return re.FindAllString(text, -1)
}
func main() {
text := "One, two three!"
text2 := "Раз, два три!"
text3 := "Jedna, dva tři čtyři pět!"
fmt.Println(getWordsFrom(text)) // [One two three]
fmt.Println(getWordsFrom(text2)) // [Раз два три]
fmt.Println(getWordsFrom(text3)) // [Jedna dva tři čtyři pět]
}? 补充说明:
- \p{L} 是 Unicode 通用类别(Unicode Category L),涵盖 Ll(小写字母)、Lu(大写字母)、Lt(首字母大写)、Lm(修饰字母)、Lo(其他字母)等全部子类,完美覆盖俄语、捷克语、中文(汉字属 \p{Lo})、阿拉伯语等;
- 若需排除数字,可简化为 [\p{L}_]+;若需兼容连字符(如 state-of-the-art),可扩展为 [\p{L}\d_]+(?:-[\p{L}\d_]+)*;
- 注意:Go 的 regexp 不支持 PCRE 风格的 \w+u 或 (?u)\w+,也不支持 \p{Word} —— 必须显式组合 Unicode 类。
✅ 最佳实践建议:
立即学习“go语言免费学习笔记(深入)”;
- 永远避免在国际化场景中直接使用 \w;
- 使用 [\p{L}\d_]+ 作为默认“单词”模式;
- 如需更严谨的分词(例如处理标点粘连、缩写、引号嵌套),建议结合 strings.FieldsFunc() 预处理或引入专用分词库(如 golang.org/x/text/unicode/norm + 自定义逻辑);
- 在正则中启用 (?i) 时,\p{L} 仍保持大小写无关性,无需额外配置。
通过替换为 Unicode 感知的字符类,即可让 Go 正则真正“读懂”世界语言。










