介绍
在上一节中,我们讨论了常用的数值类型。在本节中,我们将学习 Go 语言中的字符类型。
知识点:
- ASCII 编码
- UTF-8 编码
- Unicode 字符集
byte
rune
在上一节中,我们讨论了常用的数值类型。在本节中,我们将学习 Go 语言中的字符类型。
知识点:
byte
rune
在计算机的早期,ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)编码格式被广泛使用。它使用 7 位(bit)来表示字符,可以表示 128(2^7)个字符。其中,0 到 31 位以及 127 位表示无法显示的控制字符,而 32 到 126 位则表示日常使用的大写和小写字母、数字以及标点符号。详见表格。
随着计算机的发展,支持不同语言的需求逐渐增加。ASCII 编码无法满足这一需求。因此,不同语言开发了自己的编码格式,例如简体中文的 GB2312、韩语的 EUC-KR 以及俄语的 KOI8-R。
然而,由于世界上存在许多语言体系,需要一种能够统一所有语言的编码格式。Unicode 应运而生,满足了这一需求。
1991 年,Unicode 联盟发布了 Unicode 字符集的第一个版本。其目标是将所有语言统一为一种编码格式,使全球的计算机能够更轻松地显示和处理文本,并避免在多语言环境中的兼容性问题。
然而,Unicode 只是一个字符集;它定义了字符的编码,但并未规定这些编码的存储方式。这导致 Unicode 在很长一段时间内难以广泛采用,直到互联网的兴起。
随着互联网的不断发展,UTF-8 作为一种 Unicode 实现编码逐渐流行起来。它是一种可变长度编码,这意味着不同的符号在 UTF-8 中可以有不同的字节长度。
例如,对于英文字母(属于 ASCII 范围),它们用 1 个字节表示。字符 'y'(Unicode 值为 121)占用 1 个字节。
在日常使用中,大多数中文字符占用 3 个字节。例如,字符 '实'(Unicode 值为 23454)占用 3 个字节。
然而,也有一些中文字符占用 4 个字节。这是因为中文字符数量超过 10 万个,而根据下图所示,3 个字节只能表示略多于 6 万个字符,因此少数中文字符需要 4 个字节来表示。
UTF-8 编码的另一个优势是它向后兼容 ASCII 编码。实际上,ASCII 是 UTF-8 的子集。UTF-8 中的前 128 个字符与 ASCII 字符一一对应。这意味着原本使用 ASCII 的软件可以继续使用,几乎不需要修改。由于这些优势,UTF-8 逐渐成为首选的编码格式。
Go 编程语言的创造者 Rob Pike 和 Ken Thompson 也是 UTF-8 的发明者,因此 Go 对 UTF-8 有着特殊的亲和力。Go 要求源代码文件以 UTF-8 编码保存。在操作文本字符时,UTF-8 编码是首选。此外,标准库还提供了许多与 UTF-8 编码和解码相关的函数。
byte
是 uint8
的别名,占用一个字节(8 位)。它可以用来表示 ASCII 表中的所有字符。然而,由于 byte
能表示的值范围有限(256 或 2^8),在处理复合字符(如中文字符)时,我们需要使用 rune
类型。
创建一个名为 byte.go
的新文件,并输入以下代码:
cd ~/project
touch byte.go
package main
import "fmt"
func main() {
var a byte = 76
fmt.Printf("Value of a: %c\n", a)
var b uint8 = 76
fmt.Printf("Value of b: %c\n", b)
var c byte = 'L'
fmt.Printf("Value of c: %c\n", c)
}
运行程序后,将输出以下结果:
go run byte.go
Value of a: L
Value of b: L
Value of c: L
%c
占位符用于输出字符。可以看出,当值相同时,byte
类型和 uint8
类型会产生相同的输出。通过参考 ASCII 表,可以看到字母 'A' 的 ASCII 值为 65。当我们使用整数占位符 %d
输出该值时,结果也是 65。
因此,显而易见,Go 中的 byte
等同于整数类型中的 uint8
。rune
也是如此,但它表示不同的整数值范围。
rune
是 int32
的别名,占用四个字节(32 位)。它用于表示复合字符,例如表情符号。
更新 byte.go
文件,添加以下代码:
package main
import "fmt"
func main() {
var a rune = '😊' // 微笑表情
fmt.Printf("Value of a: %c\n", a)
var b int32 = 9829 // Unicode 字符的十进制表示(心形符号)
fmt.Printf("Value of b: %c\n", b)
var c rune = 0x1F496 // Unicode 字符的十六进制表示(闪烁的心形表情)
fmt.Printf("Value of c: %c\n", c)
var d rune = '\u0041' // 使用码点表示的 Unicode 字符(大写字母 'A')
fmt.Printf("Value of d: %c\n", d)
var e rune = '\U0001F609' // 使用码点表示的 Unicode 字符(眨眼表情)
fmt.Printf("Value of e: %c\n", e)
}
运行程序后,将显示以下输出:
go run byte.go
注意: 请在 Desktop 或 WebIDE 终端中运行程序,但避免在 LabEx VM 顶部的 Terminal Tab 中运行。
Value of a: 😊
Value of b: ♥
Value of c: 💖
Value of d: A
Value of e: 😉
注意: 在 Go 中,单引号和双引号是不同的。单引号用于表示字符,而双引号用于声明字符串。因此,在声明 byte
和 rune
类型时,应使用单引号,否则会报错。
现在,让我们巩固一下所学内容。创建一个名为 rune.go
的新文件,并输入以下代码。完成代码,使得十六进制数 0x1F648
被赋值给变量 a
,并且程序能够正确输出其值。
rune.go
应放置在 ~/project
目录中。package main
import "fmt"
func main() {
var a rune = 0x1F648
fmt.Printf("The value of a is: %c\n", a)
}
让我们回顾一下本节所学内容:
byte
数据类型可用于表示 ASCII 字符,而 rune
数据类型可用于表示 Unicode 字符。byte
数据类型可以表示 ASCII 字符,rune
数据类型可以表示 Unicode 字符。在本节中,我们首先解释了 ASCII、UTF-8 和 Unicode。然后,我们解释了字符数据类型 byte
和 rune
与整数数据类型之间的关系。