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