Golang 中的字符类型

GolangBeginner
立即练习

介绍

在上一节中,我们讨论了常用的数值类型。在本节中,你将学习 Go 语言中的字符类型。

知识点:

  • ASCII 编码
  • UTF-8 编码
  • Unicode 字符集
  • byte
  • rune

ASCII 编码

在计算机发展的早期,使用的是 ASCII(美国信息交换标准代码)编码格式。它使用 7 位二进制位来表示字符,总共可以表示 128(2^7)个字符。其中,从 0 到 31 位以及第 127 位表示不可显示的控制字符,而从 32 到 126 位则表示日常使用的英文大小写字母、数字和标点符号。详情请查看表格

随着计算机的普及,支持不同语言的需求日益增长。ASCII 编码已不足以满足这一需求。因此,不同语言开发了各自的编码格式,例如简体中文的 GB2312、韩文的 EUC-KR 和俄文的 KOI8-R。

然而,由于世界上存在众多的语系,迫切需要一种能够统一所有语言的单一编码格式。Unicode(万国码)便应运而生。

Unicode 字符集

1991 年,Unicode 联盟发布了 Unicode 字符集的第一个版本。其目标是将所有语言统一到一种编码格式中,使全球的计算机能够更轻松地显示和处理文本,并避免多语言环境下的兼容性问题。

然而,Unicode 最初只是一个字符集;它定义了字符的代码点,但并未规定这些代码点在计算机中如何存储。这导致它在很长一段时间内难以被广泛采用,直到互联网的兴起。

UTF-8 编码

随着互联网的不断发展,作为 Unicode 实现方式之一的 UTF-8 编码逐渐流行起来。它是一种变长编码,这意味着在 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 和 rune

byteuint8 的别名,占用一个字节(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 等同于整数类型中的 uint8rune 也是同样的道理,但它代表了不同范围的整数值。

runeint32 的别名,占用四个字节(32 位)。它用于表示复合字符,例如表情符号(emoji)。

使用以下代码更新 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

注意: 请在桌面环境或 WebIDE 的终端中运行程序,避免在 LabEx 虚拟机顶部的终端选项卡中运行。

Value of a: 😊
Value of b: ♥
Value of c: 💖
Value of d: A
Value of e: 😉
  • 变量 a 代表微笑表情 '😊'。
  • 变量 b 使用 Unicode 字符的十进制表示(9829)进行初始化,对应心形符号 '♥'。
  • 变量 c 使用 Unicode 字符的十六进制表示(0x1F496)进行初始化,对应闪烁的心表情 '💖'。
  • 变量 d 使用 Unicode 格式 \u0041 表示大写字母 'A'。
  • 变量 e 使用 \U 格式配合代码点 0001F609 表示眨眼表情 '😉'。

注意: 在 Go 中,单引号和双引号的含义不同。单引号用于表示字符,而双引号用于声明字符串。因此,在声明 byterune 类型时必须使用单引号,否则会报错。

挑战

现在,让我们巩固所学知识。创建一个名为 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)
}
✨ 查看解决方案并练习

总结

让我们回顾一下本节所学的内容:

  • ASCII 字符占用一个字节,可以表示 128 个字符。
  • UTF-8 编码是 Unicode 字符集的一种实现形式。它是一种变长编码,其字节长度取决于所表示的字符。
  • byte 数据类型可用于表示 ASCII 字符,而 rune 数据类型可用于表示 Unicode 字符。
  • byte 类型本质上是 uint8,而 rune 类型本质上是 int32

在本节中,我们首先解释了 ASCII、UTF-8 和 Unicode。随后,我们阐述了字符数据类型 byterune 与整数类型之间的关系。