本文详细介绍Golang中的两种字符类型rune和byte,介绍他们的区别,编码方式和简单的使用。
文章目录
- [`byte` 类型](#
byte
类型)- [`rune` 类型](#
rune
类型)- [UTF-8 与 Unicode 的关系](#UTF-8 与 Unicode 的关系)
- byte和rune的主要区别
- Go的默认编码方式
- 遍历方式
- [遍历 `byte`](#遍历
byte
)- [遍历 `rune`](#遍历
rune
)- 补充
- 字符还原
- [从 `byte` 序列还原字符串](#从
byte
序列还原字符串)- [从 `rune` 序列还原字符串](#从
rune
序列还原字符串)
在Go语言中,rune
和byte
都是表示单个字符的类型,但它们有一些关键的区别。
byte
类型
byte
是 uint8
的别名,即一个 8 位无符号整数,表示一个字节,范围是 0 到 255。
byte
用于表示 UTF-8 编码中的 字节,适合处理字节流和 ASCII 字符。
字符占用字节数:
- ASCII 字符(0-127)占用 1 字节。
- 常见的字符,如拉丁字母、标点符号,占用 1 字节。
- 中文等非 ASCII 字符会占用 3 字节。
byte
表示:字符串 "你"
,它在 Go 中的 UTF-8 编码是 0xE4, 0xBD, 0xA0
(十六进制)。
go
s := "你"
for i := 0; i < len(s); i++ {
fmt.Printf("byte at index %d: %d\n", i, s[i])
}
输出:
byte at index 0: 228
byte at index 1: 189
byte at index 2: 160
rune
类型
rune
是 int32
的别名,即一个 32 位有符号整数,用于表示一个 Unicode 字符。Go中所有字符(包括 ASCII 和 Unicode 字符)都是以 rune
类型表示的,范围是 0 到 0x10FFFF。
rune
用于表示 Unicode 字符 ,它表示字符的 编码点,适合处理字符操作,尤其是涉及 Unicode 字符(如中文、表情符号等)。
rune
表示:
go
s := "你"
for _, c := range s {
fmt.Printf("rune: %c, rune value: %d\n", c, c)
}
输出:
rune: 你, rune value: 20320
这表示 "你"
的 Unicode 编码点(20320
,即 0x4F60
)被 rune
类型存储。
UTF-8 与 Unicode 的关系
- Unicode 是字符集 ,而 UTF-8 是 Unicode 字符集的编码方式之一。Unicode 定义了所有字符的编码点,但它并没有规定字符如何存储和传输。为了实现跨平台和跨语言的兼容,UTF-8 被定义为一种将 Unicode 编码点转换为字节序列的方式。除了 UTF-8 外,还有 UTF-16 和 UTF-32。
- 联系 :
- Unicode 为每个字符分配一个编码点(一个数字)。
- UTF-8 通过不同长度的字节序列来编码这些 Unicode 编码点,使得它们可以被存储在文件中、传输通过网络、显示在屏幕上等。
byte和rune的主要区别
特性 | byte |
rune |
---|---|---|
类型 | uint8 (8-bit unsigned int) |
int32 (32-bit signed int) |
用途 | 处理 ASCII 或字节数据 | 处理 Unicode 字符 |
表示范围 | 0 到 255 | 0 到 0x10FFFF |
常见应用 | 字节流、ASCII字符 | Unicode字符(包括多字节字符) |
存储大小 | 1 字节 | 4 字节 |
字符集支持 | 仅支持 ASCII 字符 | 支持所有 Unicode 字符 |
Go的默认编码方式
Go 字符串默认的编码方式是 UTF-8 。所以默认使用 byte
序列来表示字符串中的每个字符。
具体来说,Go 中的字符串(string
类型)是由 UTF-8 编码的字节序列 组成的。因此:
- 一个 Go 字符串是由多个字节(
byte
)组成的,每个字节都是 UTF-8 编码的一个字符。 - 这些字节遵循 UTF-8 编码,Go 字符串既可以包含 ASCII 字符(这些字符在 UTF-8 中占用 1 个字节),也可以包含多字节的 Unicode 字符(如中文字符,这些字符在 UTF-8 中通常占用 3 个字节)。
go
s := "a"
fmt.Print("占用字节数:", len(s))
fmt.Printf(";类型:%T ", s[0])
fmt.Println()
s1 := "你"
fmt.Print("占用字节数:", len(s1))
fmt.Printf(";类型:%T ", s1[0])
输出:
占用字节数:1;类型:uint8
占用字节数:3;类型:uint8
遍历方式
遍历 byte
bytes := []byte(s)
可以直接将字符串转为byte
,当然也可以遍历:
- 使用
for i := 0; i < len(s); i++
,每次迭代都可以访问字符串中的每个字节。 len(s)
返回字符串的 字节数(byte) ,即字符串中包含的字节总数,而不是字符的数量。对于一个包含多字节字符(如中文字符)的字符串,len(s)
返回的是字符串所占用的字节数。
go
package main
import "fmt"
func main() {
s := "你" // 包含中文字符
// 按字节遍历字符串
fmt.Println("按字节遍历字符串:")
for i := 0; i < len(s); i++ {
fmt.Printf("s[%d] = %v (类型: %T)\n", i, s[i], s[i]) // 输出每个字节的值
}
}
输出:
按字节遍历字符串:
s[0] = 228 (类型: uint8)
s[1] = 189 (类型: uint8)
s[2] = 160 (类型: uint8)
遍历 rune
runes := []rune(s)
可以直接将字符串转为rune
,当然也可以遍历:
-
使用
for _, c := range s
遍历字符串时,Go 会自动将字符串s
中的每个字符解码成rune
类型,这样即使字符是多字节的,也能正确处理。 -
range
遍历字符串时,按 字符(rune) 进行迭代。每次迭代返回一个 Unicode 码点(rune) 和该字符在字符串中的索引。对于多字节字符,range
会自动跳过这些字节,按字符来迭代。
go
package main
import "fmt"
func main() {
s := "你"
// len(s) 返回字节数
fmt.Println("len(s) =", len(s)) // 输出:3,因为"你"是由 3 个字节表示
// 使用 range 遍历字符串,按字符(rune)遍历
fmt.Println("使用 range 遍历字符串,按字符(rune)遍历:")
for i, r := range s {
fmt.Printf("i = %d, r = %v (类型: %T)\n", i, r, r)
}
}
输出:
len(s) = 3
使用 range 遍历字符串,按字符(rune)遍历:
i = 0, r = 20320 (类型: int32)
补充
for i := range s
的 s[i] 其实也是byte
,但是处理中文时候会存在问题。
当你使用
for i := range s
处理英文字符串的时候,可能不会有问题,因为英文字符(ASCII 字符)在 UTF-8 编码中是单字节表示的,所以每个字符正好对应一个字节。但是如果字符串中包含非英文字符(如中文、表情符号等),它们通常会占用多个字节。在这种情况下。使用
for i := range s
就会发现问题,range
会按照字符(rune
)进行遍历,统计的数量是字符数(rune
)【如下只有1个】,而不是字节数(byte
)【一个中文,应该是对应3个字节】。
go
package main
import "fmt"
func main() {
s := "你" // 字符串包含中文字符
// 使用 range 遍历字符串
fmt.Println("使用 range 遍历字符串:")
for i := range s {
fmt.Printf("s[%d] = %v (类型: %T)\n", i, s[i], s[i]) // 打印每个字节的值
}
}
输出:
使用 range 遍历字符串:
s[0] = 228 (类型: uint8)
字符还原
要从 byte
序列或 rune
序列还原回原始字符串,你可以通过以下方式进行操作:
- 从
byte
序列还原字符串 :可以直接使用string(byteSlice)
。 - 从
rune
序列还原字符串 :可以直接使用string(runeSlice)
。
从 byte
序列还原字符串
go
package main
import "fmt"
func main() {
s := "你好" // 字符串 "你好"
// 将字符串转换成 rune 切片
bytes := []byte(s)
fmt.Println("bytes:", bytes)
// 将 rune 切片转换回字符串
s1 := string(bytes)
fmt.Println("还原的字符串:", s1)
}
bash
bytes: [228 189 160 229 165 189]
还原的字符串: 你好
从 rune
序列还原字符串
go
package main
import "fmt"
func main() {
s := "你好" // 字符串 "你好"
// 将字符串转换成 rune 切片
runes := []rune(s)
fmt.Println("runes编码:", runes)
// 将 rune 切片转换回字符串
s1 := string(runes)
fmt.Println("还原的字符串:", s1)
}
bash
runes编码: [20320 22909]
还原的字符串: 你好