Go语言中多字节字符的处理方法详解

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

完!

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Go语言range关键字循环时的坑

    关键字range可用于循环,类似迭代器操作,它可以遍历slice,array,string,map和channel,然后返回索引或值.可以使用"_"来忽略不想要的返回值.可以方便的读取上面类型中的内容,例如: package main import "fmt" func main() { str1 := []string{"1", "2", "3", "4"} for key, valu

  • go语言中int和byte转换方式

    主机字节序 主机字节序模式有两种,大端数据模式和小端数据模式,在网络编程中应注意这两者的区别,以保证数据处理的正确性:例如网络的数据是以大端数据模式进行交互,而我们的主机大多数以小端模式处理,如果不转换,数据会混乱 参考 :一般来说,两个主机在网络通信需要经过如下转换过程:主机字节序 -> 网络字节序 -> 主机字节序 大端小端区别 大端模式:Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端 低地址 --------------------> 高地址 高

  • Go语言的JSON处理详解

    Go语言内建对JSON的支持.使用Go语言内置的encoding/json标准库,开发者可以轻松使用Go程序生成和解析JSON格式的数据.在Go语言实现JSON的编码和解码时,遵循RFC4627协议标准. 1.编码为JSON格式 使用json.Marshal()函数可以对一组数据进行JSON格式的编码.json.Marshal()函数的声明如下: 假如有如下一个Book类型的结构体: 并且有如下一个Book类型的实例对象: 然后,我们可以使用json.Marshal()函数将gobook实例生成

  • go语言中strings包的用法汇总

    strings 包中的函数和方法 // strings.go ------------------------------------------------------------ // Count 计算字符串 sep 在 s 中的非重叠个数 // 如果 sep 为空字符串,则返回 s 中的字符(非字节)个数 + 1 // 使用 Rabin-Karp 算法实现 func Count(s, sep string) int func main() { s := "Hello,世界!!!!!&quo

  • Go语言中 Channel 详解

    Channel是Go中的一个核心类型,你可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication). 它的操作符是箭头 <- . ch <- v    // 发送值v到Channel ch中 v := <-ch  // 从Channel ch中接收数据,并将数据赋值给v (箭头的指向就是数据的流向) 就像 map 和 slice 数据类型一样, channel必须先创建再使用: ch := make(chan int) Channel类型 Cha

  • Go语言中的字符串处理方法示例详解

    1 概述 字符串,string,一串固定长度的字符连接起来的字符集合.Go语言的字符串是使用UTF-8编码的.UTF-8是Unicode的实现方式之一. Go语言原生支持字符串.使用双引号("")或反引号(``)定义. 双引号:"", 用于单行字符串. 反引号:``,用于定义多行字符串,内部会原样解析. 示例: // 单行 "心有猛虎,细嗅蔷薇" // 多行 ` 大风歌 大风起兮云飞扬. 威加海内兮归故乡. 安得猛士兮守四方! ` 字符串支持转义

  • Go语言实现互斥锁、随机数、time、List

    Go语言实现互斥锁.随机数.time.List import ( "container/list" "fmt" "math/rand" //备注2:随机数的包 "sync" //备注1:异步任务的包 "time" ) type INFO struct { lock sync.Mutex //备注1:异步锁 Name string Time int64 } var List *list.List = list

  • Golang数组的传递详解

    概念介绍 数组与切片 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列.数组长度最大为2Gb,它是值类型.切片是对数组一个连续片段的引用,所以切片是一个引用类型. 按值传递和按引用传递 Go语言中函数的参数有两种传递方式,按值传递和按引用传递.Go默认使用按值传递来传递参数,也就是传递参数的副本.在函数中对副本的值进行更改操作时,不会影响到原来的变量. 按引用传递其实也可以称作"按值传递",只不过该副本是一个地址的拷贝,通过它可以修改这个值所指向的地址上的值. Go语言中,在函

  • Go语言的文件操作代码汇总

    # 建立与打开文件 // 新建文件可以通过如下两个方法: func Create(name string) (file *File, err Error) 根据提供的文件名创建新的文件,返回一个文件对象,默认权限是0666的文件,返回的文件对象是可读写的. func NewFile(fd uintptr, name string) *File 根据文件描述符创建相应的文件,返回一个文件对象 // 通过如下两个方法来打开文件: func Open(name string) (file *File,

  • Go语言中http和mysql的实现代码

    http 编程 Go 原生支持http: import "net/http" Go 的http服务性能和nginx比较接近: 就是说用Go写的Web程序上线,程序前面不需要再部署nginx的Web服务器,这里省掉的是Web服务器.如果服务器上部署了多个Web应用,还是需要反向代理的,一般这也是nginx或apache. 几行代码就可以实现一个web服务: package main import ( "fmt" "net/http" ) func

随机推荐