
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 正则真正“读懂”世界语言。