提起 Go 语言中的rune类型,相信大家对它并不陌生。虽然它并不常用,但在我的印象里,用得最多的就是用它来处理中文字符串截取。
没错,多语言的字符串处理就是rune的强项。
rune是 Go 的内置类型之一,占用4个字节,通常用于表示Unicode字符,它是int32的别名,所以它在所有方面和int32等价。
// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
type rune = int32
定义与使用
rune用来表示单个Unicode字符,字符不同于字符串,字符串使用双引号表示,而字符使用单引号表示:
package main
import (
"fmt"
"unsafe"
)
func main() {
var a rune = 'a'
var b rune = '你'
fmt.Printf("a=%d, size=%d\n", a, unsafe.Sizeof(a)) // output: a=97, size=4
fmt.Printf("b=%d, size=%d\n", b, unsafe.Sizeof(b)) // output: b=20320, size=4
}
字符串和rune的转换
字符串是由多个字符组成,可以使用[]rune切片来表示,string类型和[]rune可以互相转换:
package main
import "fmt"
func main() {
s := "Hello, 技术圈"
b := []rune(s)
c := string(b)
fmt.Printf("%v, len=%d\n", s, len(s))
// output: Hello, 技术圈, len=16
fmt.Printf("%v, len=%d\n", b, len(b))
// output: [72 101 108 108 111 44 32 25216 26415 22280], len=10
fmt.Printf("%v, len=%d\n", c, len(c))
// output: Hello, 技术圈, len=16
}
在UTF-8编码下,一个汉字占 3 个字节,所以字符串长度为 16 = 5+1+1+9。
而转换为[]rune的长度就是 10 = 5+1+1+3,这就是计算的字符的长度,换句话说就是字符的个数,一个汉字算一个字符。
string、byte、rune的关系
字符串(string)是一个有广泛应用的场景的内置类型,是多个字符连在一起组成,默认以 UTF-8 编码存储 Unicode 字符,长度不可变,修改需要重新分配内存。
而字节(byte)是uint8的别名用来表示一个字节,表示原始字节或 ASCII 字符(0~255),字符串的本质就是多个字节组成的字节数组([]byte),在 UTF-8 编码下,一个字母占用 1 个字节,一个汉字占 3 个字节。
rune 占用 4 个字节,表示一个 Unicode 字符,[]rune表示多个字符,可以string直接互相转换。
| 特性 | string | byte | rune |
|---|---|---|---|
| 本质 | 只读的[]byte | uint8 | int32 |
| 占用字节 | 固定长度 | 1个字节 | 4个字节 |
| 表示 | 字符串 | ASCII/单字节字符 | 多字节字符 |
总结一下rune的使用场景:
- 计算字符长度
- 安全的字符截取、反转等
- emoji 表情符号处理
- 处理多语言文本
- 字符串语法分析/解析器
最后
之所有要有rune类型,是因为 Go 中的字符串是字节序列,像中文、日文、emoji等字符可能占多个字节,普通的字符串或[]byte用来处理多个字节的字符很不方便。
所以,用rune表示单个字符,用[]rune处理字符串中的多个字符更加方便、完整,不会出现乱码现象。