unicode.isletter 对中文返回 false 不是 bug,因旧版 go(1.18 及以前)未包含 lo 类别;应统一用 unicode.is(unicode.letter, r) 或 unicode.is(unicode.han, r) 精准判断。

unicode.IsLetter 对中文返回 false,不是 bug,是它只覆盖 Ll、Lu、Lt 等子类,而汉字属于 Lo(Letter, other),默认不包含在内。真正能通吃中日韩文字的判断方式是 unicode.Is(unicode.Letter, r)。
为什么 unicode.IsLetter 判不了汉字却还被广泛误用
很多人写完 for _, r := range s { if unicode.IsLetter(r) { ... } } 发现中文没进分支,第一反应是“函数坏了”或“Go 不支持中文”。其实问题出在对 Unicode 类别体系的理解偏差:unicode.IsLetter 是一个快捷封装,等价于 unicode.IsOneOf([]*unicode.RangeTable{unicode.Ll, unicode.Lu, unicode.Lt, unicode.Lm, unicode.Lo}, r) —— 等等,Lo 不就在里面?
但注意:Go 1.18 及更早版本的 unicode.IsLetter **确实不含 Lo**;直到 Go 1.19 才把 Lo 加进去。如果你用的是旧版本(比如某些嵌入式环境或遗留 CI),unicode.IsLetter('中') 就会稳定返回 false。
- 查你当前 Go 版本:
go version,若低于1.19,别信文档里“支持汉字”的说法 - 兼容写法统一用
unicode.Is(unicode.Letter, r),它从 Go 1.0 就包含完整Letter范围表 -
unicode.Letter是一个*unicode.RangeTable,不是字符串或常量,不能直接比较
unicode.Is(unicode.Han, r) 是唯一靠谱的“纯汉字”判断
没有 unicode.IsHan,这是高频误解。标准库只提供脚本(Script)和区块(Block)级别的预定义范围表,unicode.Han 就是其中之一,覆盖中日韩越共用汉字(含 Ext-B/C/D/E),但不包括平假名、片假名、谚文——它们分别对应 unicode.Hiragana、unicode.Katakana、unicode.Hangul。
- 要筛出“仅汉字”,用
unicode.Is(unicode.Han, r),不是unicode.IsLetter - 要筛出“汉字 + 假名 + 谚文”,得组合:
unicode.Is(unicode.Han, r) || unicode.Is(unicode.Hiragana, r) || unicode.Is(unicode.Katakana, r) || unicode.Is(unicode.Hangul, r) -
unicode.Is(unicode.Han, r)在 Go 1.19+ 才完整支持扩展区;若需兼容旧版,可手动补范围:r >= 0x4E00 && r (基本汉字区)+ 其他区间
unicode.IsDigit 和 unicode.IsNumber 的关键区别
unicode.IsDigit 只认 Unicode 标准里的 Decimal_Number 类字符,即有明确十进制数值含义的码点(如 ASCII '0'–'9'、阿拉伯-印地数字 '٠'–'٩')。而 unicode.IsNumber 范围更广,覆盖 Decimal_Number、Letter_Number(如罗马数字 'Ⅲ')、Other_Number(如上标 '⁵'、分数 '⅒')。
- 做表单校验(只接受 0–9),用
r >= '0' && r 最快最安全,绕过 Unicode 查表开销 - 做 OCR 或多语言文本分析(要识别“Ⅳ”“①”“²”),必须用
unicode.IsNumber - 别用
unicode.IsDigit判断全角数字'0'(U+FF10):它属于Other_Number,IsDigit返回false,但IsNumber返回true
遍历字符串时踩得最多的坑:别用 s[i] 当 rune
Go 字符串底层是 UTF-8 字节序列。s[0] 拿到的是第一个字节,不是第一个字符。对中文、emoji 这类多字节 rune,s[0] 可能是 0xE4(UTF-8 首字节),传给 unicode.IsLetter 会 panic 或恒返回 false。
- 正确姿势只有两种:
for _, r := range s { ... }(推荐)或rs := []rune(s); for i := range rs { r := rs[i]; ... } - 别写
for i := 0; i —— 这是绝大多数线上 panic 的源头之一 - 如果真需要索引操作(比如跳过前 N 个字符),先转
[]rune,再按rune索引;别试图用utf8.RuneCountInString算字节数再切片
''(软连字符)既是 Mark_NonSpacing 又是 Other_Format),而 unicode.Is 只认它是否落在指定 *RangeTable 内。实际项目里,别死磕单个函数,优先查 UnicodeData.txt 或用 unicode.Category(r) 打印具体类别再决定逻辑分支。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











