提起 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
处理字符串中的多个字符更加方便、完整,不会出现乱码现象。