Go语言中多字节字符的处理

hanzkering · · 1549 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

## 1 概述 Go语言的字符串是使用 `UTF-8` 编码的。`UTF-8` 是 `Unicode` 的实现方式之一。本文内容包括:`UTF-8` 和 `Unicode` 的关系,Go语言提供的 `unicode` 包和 `unicode/utf8` 包的使用。 ## 2 `UTF-8` 和 `Unicode` 的关系 `Unicode`一种字符集,是国际标谁化组织(ISO)设计的一个包括了地球上所有文化、所有字母和符号 的编码。他们叫它 `Universal Multiple-Octet Coded Character Set`,简称 UCS,也就是 `Unicode`。`Unicode` 为每一个 字符 分配一个唯一的 码点(Code Point),就是一个唯一的值。例如 康 的码点就是 24247,十六进制为 `5eb7`。 `Unicode` 字符集仅仅定义了字符与码点的对应关系,但是并没有定义该如何编码(存储)这个码值,这就导致了很多问题。例如由于字符的码值不同,导致所需要的存储空间是不一致的,计算机不能确定接下来的字符是占用几个字节。还有就是如果采用固定的长度假设都是4个字节来存储码点值,那么会导致空间的额外浪费,因为 `ascii` 码字符其实仅仅需要一个字节的空间。 `UTF-8` 就是解决如何为 `Unicode` 编码而设计的一种编码规则。可以说 `UTF-8` 是 `Unicode` 的实现方式之一。其特点是一种变长编码,使用1到4个字节表示一个字符,根据不同的符号而变化长度。`UTF-8` 的编码规则有二: * 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 `Unicode` 码。因此对于ASCII码字符,`UTF-8` 编码和 `ASCII` 码是相同的。 * 对于 n 字节的符号(n > 1,2到4),第一个字节的前n位都设为1,第n + 1 位设为 0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 `Unicode` 码。 以下是编码规则: ``` Unicode | UTF-8 --------------------------------------------------------- 0000 0000-0000 007F | 0xxxxxxx 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx --------------------------------------------------------- ``` Go语言中,对于 Unicode 和 UTF-8 使用了 `unicode` 和 `unicode/utf8` 包来实现,下面是阅读 API 的总结和说明。 ## 3 `Unicode` 包 Go语言中,提供了 `Unicode` 包,处理与 `Unicode` 相关的操作,整理如下: ### Is(rangeTab \*RangeTable, r rune) bool 检测 rune r 是否在 `rangeTable` 指定的字符范围内。 `rangeTable` 一个 `Unicode` 码值集合,通常使用 `unicode` 包中定义的集合。 ``` 判断字符是否出现在汉字集合中: unicode.Is(unicode.Scripts["Han"], 'k') // 返回 false unicode.Is(unicode.Scripts["Han"], '康') // 返回 true ``` ### In(r rune, ranges ...\*RangeTable) bool 检测 rune r 是否在多个 `rangeTable` 指定的字符范围内。 `rangeTable` 一个 `Unicode` 码值集合,通常使用 `unicode` 包中定义的集合。 ``` unicode.In('康', unicode.Scripts["Han"], unicode.Scripts["Latin"]) // 返回 true unicode.In('k', unicode.Scripts["Han"], unicode.Scripts["Latin"]) // 返回 true ``` ### IsOneOf(ranges []\*RangeTable, r rune) bool 检测 rune r 是否在 `rangeTable` ranges 指定的字符范围内。与 `In` 功能类似,推荐使用 `In`。 ### IsSpace(r rune) bool 检测字符 rune r 是否是空白字符。在Latin-1字符空间中,空白字符为: ``` '\t', '\n', '\v', '\f', '\r', ' ', U+0085 (NEL), U+00A0 (NBSP) ``` 其它的空白字符请参见策略Z和属性Pattern_White_Space。 ### IsDigit(r rune) bool 检测字符 rune r 是否是十进制数字字符。 ``` unicode.IsDigit('9') // 返回 true unicode.IsDigit('k') // 返回 false ``` ### IsNumber(r rune) bool 检测字符 rune r 是否是 `Unicode` 数字字符。 ### IsLetter(r rune) bool 检测一个字符 rune r 是否是字母 ``` unicode.IsLetter('9') // 返回 false unicode.IsLetter('k') // 返回 true ``` ### IsGraphic(r rune) bool 一个字符 rune r 是否是 unicode 图形字符。图形字符包括字母、标记、数字、符号、标点、空白。 ``` unicode.IsGraphic('9') // 返回 true unicode.IsGraphic(',') // 返回 true ``` ### IsControl(r rune) bool 检测一个字符 rune r 是否是 unicode 控制字符。 ### IsMark(r rune) bool 检测一个字符 rune r 是否是标记字符。 ### IsPrint(r rune) bool 检测一个字符 rune r 是否是的可打印字符,基本与图形字符一致,除ASCII空白字符U+0020。 ### IsPunct(r rune) bool 检测一个字符 rune r 是否是 unicode标点字符。 ``` unicode.IsPunct('9') // 返回 false unicode.IsPunct(',') // 返回 true ``` ### IsSymbol(r rune) bool 检测一个字符 rune r 是否是 unicode 符号字符。 ### IsLower(r rune) bool 检测一个字符 rune r 是否是小写字母。 ``` unicode.IsLower('h') // 返回 true unicode.IsLower('H') // 返回 false ``` ### IsUpper(r rune) bool 检测一个字符 rune r 是否是大写字母。 ``` unicode.IsUpper('h') // 返回 false unicode.IsUpper('H') // 返回 true ``` ### IsTitle(r rune) bool 检测一个字符 rune r 是否是Title字符。大部分字符的 Title 格式就是其大写格式,少数字符的 Title 格式是特殊字符,例如 `ᾏᾟᾯ`。 ``` unicode.IsTitle('ᾯ') // 返回 true unicode.IsTitle('h') // 返回 false unicode.IsTitle('H') // 返回 true ``` ### To(\_case int, r rune) rune 将字符 rune r 转换为指定的格式,格式_case支持:unicode.UpperCase、unicode.LowerCase、unicode.TitleCase ``` unicode.To(unicode.UpperCase, 'h') // 返回 H ``` ### ToLower(r rune) rune 将字符 rune r 转换为小写。 ``` unicode.ToLower('H') // 返回 h ``` ### func (SpecialCase) ToLower 将字符 rune r 转换为小写。优先使用映射表 SpecialCase。 映射表 SpecialCase 是特定语言环境下大小写的映射表。主要应用于一些欧洲字符,例如土耳其 TurkishCase。 ``` unicode.TurkishCase.ToLower('İ') // 返回 i ``` ### ToUpper(r rune) rune 将字符 rune r 转换为大写。 ``` unicode.ToUpper('h') // 返回 H ``` ### func (SpecialCase) ToUpper 将字符 rune r 转换为大写。优先使用映射表 SpecialCase。 映射表 SpecialCase 是特定语言环境下大小写的映射表。主要应用于一些欧洲字符,例如土耳其 TurkishCase。 ``` unicode.TurkishCase.ToUpper('i') // 返回 İ ``` ### ToTitle(r rune) rune 将字符 rune r 转换为 Title 字符。 ``` unicode.ToTitle('h') // 返回 H ``` ### func (SpecialCase) ToTitle 将字符 rune r 转换为 Title 字符。优先使用映射表 SpecialCase。 映射表 SpecialCase 是特定语言环境下大小写的映射表。主要应用于一些欧洲字符,例如土耳其 TurkishCase。 ``` unicode.TurkishCase.ToTitle('i') // 返回 İ ``` ### SimpleFold(r rune) rune 在 unicode 标准字符映射中查找与 rune r 互相对应的 unicode 码值。向码值大的方向循环查找。互相对应指的是同一个字符可能出现的多种写法。 ``` unicode.SimpleFold('H') // 返回 h unicode.SimpleFold('Φ')) // 返回 φ ``` ## 4 `unicode/utf8` 包 ### DecodeLastRune(p []byte) (r rune, size int) 解码 []byte p 中最后一个 UTF-8 编码序列,返回该码值和长度。 ``` utf8.DecodeLastRune([]byte("小韩说课")) // 返回 35838 3 // 35838 就是课的 unicode 码值 ``` ### DecodeLastRuneInString(s string) (r rune, size int) 解码 string s 中最后一个 UTF-8 编码序列,返回该码值和长度。 ``` utf8.DecodeLastRuneInString("小韩说课") // 返回 35838 3 // 35838 就是课的 unicode 码值 ``` ### DecodeRune(p []byte) (r rune, size int) 解码 []byte p 中第一个 UTF-8 编码序列,返回该码值和长度。 ``` utf8.DecodeRune([]byte("小韩说课")) // 返回 23567 3 // 23567 就是 小 的 unicode 码值 ``` ### DecodeRuneInString(s string) (r rune, size int) 解码 string s 中第一个 UTF-8 编码序列,返回该码值和长度。 ``` utf8.DecodeRuneInString("小韩说课") // 返回 23567 3 // 23567 就是 小 的 unicode 码值 ``` ### EncodeRune(p []byte, r rune) int 将 rune r 的 UTF-8 编码序列写入 []byte p,并返回写入的字节数。p 满足足够的长度。 ``` buf := make([]byte, 3) n := utf8.EncodeRune(buf, '康') fmt.Println(buf, n) // 输出 [229 186 183] 3 ``` ### FullRune(p []byte) bool 检测 []byte p 是否包含一个完整 UTF-8 编码。 ``` buf := []byte{229, 186, 183} // 康 utf8.FullRune(buf) // 返回 true utf8.FullRune(buf[:2]) // 返回 false ``` ### FullRuneInString(s string) bool 检测 string s 是否包含一个完整 UTF-8 编码。 ``` buf := "康" // 康 utf8.FullRuneInString(buf) // 返回 true utf8.FullRuneInString(buf[:2]) // 返回 false ``` ### RuneCount(p []byte) int 返回 []byte p 中的 UTF-8 编码的码值的个数。 ``` buf := []byte("小韩说课") len(buf) // 返回 12 utf8.RuneCount(buf) // 返回 4 ``` ### RuneCountInString(s string) (n int) 返回 string s 中的 UTF-8 编码的码值的个数。 ``` buf := "小韩说课" len(buf) // 返回 12 utf8.RuneCountInString(buf) // 返回 4 ``` ### RuneLen(r rune) int 返回 rune r 编码后的字节数。 ``` utf8.RuneLen('康') // 返回 3 utf8.RuneLen('H') // 返回 1 ``` ### RuneStart(b byte) bool 检测字节 byte b 是否可以作为某个 rune 编码的第一个字节。 ``` buf := "小韩说课" utf8.RuneStart(buf[0]) // 返回 true utf8.RuneStart(buf[1]) // 返回 false utf8.RuneStart(buf[3]) // 返回 true ``` ### Valid(p []byte) bool 检测切片 []byte p 是否包含完整且合法的 UTF-8 编码序列。 ``` valid := []byte("小韩说课") invalid := []byte{0xff, 0xfe, 0xfd} utf8.Valid(valid) // 返回 true utf8.Valid(invalid) // 返回 false ``` ### ValidRune(r rune) bool 检测字符 rune r 是否包含完整且合法的 UTF-8 编码序列。 ``` valid := 'a' invalid := rune(0xfffffff) fmt.Println(utf8.ValidRune(valid)) // 返回 true fmt.Println(utf8.ValidRune(invalid)) // 返回 false ``` ### ValidString(s string) bool 检测字符串 string s 是否包含完整且合法的 UTF-8 编码序列。 ``` valid := "小韩说课" invalid := string([]byte{0xff, 0xfe, 0xfd}) fmt.Println(utf8.ValidString(valid)) // 返回 true fmt.Println(utf8.ValidString(invalid)) // 返回 false ``` 完! 原文出自:[小韩说课](http://www.hellokang.net/go/go-utf8/) 微信关注:小韩说课 ![小韩说课](http://www.hellokang.net/images/wechat_subscription.jpg)

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1549 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传