应先用 norm.NFC 归一化再逐 rune 调用 unicode.IsLetter,unicode.IsDigit 仅识别 Nd 类数字,中文等用 unicode.Is(unicode.Han, r) 判断,计数需用 grapheme 包而非 len([]rune(s))。
unicode.IsLetter 和 unicode.IsDigit 怎么用才不踩空格或 Unicode 变体的坑
这两个函数只判断 Unicode 码点所属的通用类别,unicode.IsLetter 对所有 L* 类别(如 Ll、Lu、Lt)返回 true,但不会过滤掉带变音符号的组合字符(如 é 拆成 e + ◌́ 时,后者被判定为 Mn,unicode.IsLetter('e') 是 true,但 unicode.IsLetter('◌́') 是 false)。实际做输入校验时,常误以为“整个字符串是字母”就调一遍 IsLetter —— 忽略了组合字符、零宽连接符、双向控制符等。
实操建议:
- 若需判断「用户视觉上看到的一个字是否为字母」,先用
norm.NFC归一化字符串再逐 rune 检查 -
unicode.IsDigit只识别Nd类(如阿拉伯数字、全角数字 123),不识别罗马数字或上标数字⁰¹²;需要兼容时得额外查表 - 中文、日文汉字属于
Lo(Other Letter),unicode.IsLetter能正确识别,但unicode.IsLower/unicode.IsUpper对它们一律返回false
为什么 strings.Map + unicode.ToLower 不等于 strings.ToLower
strings.ToLower 是语言感知的:它会根据当前 locale 处理特殊映射(比如土耳其语中 I → i,但 İ → i,而 I → ı);而 strings.Map(unicode.ToLower, s) 仅做 Unicode 简单小写映射(Simple Lowercase Mapping),跳过上下文敏感规则和语言特例。
常见错误现象:在土耳其系统或处理土耳其语用户输入时,用 strings.Map(unicode.ToLower, "I") 得到 "i",但正确结果应为 "ı"(无点 i)。
实操建议:
- 一般文本转换优先用
strings.ToLower或strings.ToUpper - 若需严格按 Unicode 标准做无上下文转换(如标识符规范化),再用
unicode.ToLower配合strings.Map - 注意:某些字符小写后长度会变(如
ß→"ss"),strings.Map无法处理这种多对一/一对多映射,此时必须用cases包(Go 1.13+)
如何安全判断一个 rune 是否属于中文、日文或韩文字符范围
unicode 标准库本身不提供“是否中文”这样的高层语义判断,它只暴露码点分类(如 unicode.Han、unicode.Hiragana、unicode.Katakana、unicode.Hangul),这些是 Unicode 脚本(Script)属性,需用 unicode.Is 配合具体脚本常量。