字符与布尔值
计算机中的“字符”并非我们日常所指的单个文字。要表示一个文字,首先需要通过特定的编码方案将其映射到唯一的码位(Code Point),然后将码位转换为字节序列进行存储。
布尔值通常通过二进制的 0 和 1 来表示。
字符
我们通常将“字符”简单地理解为可读、可操作的单个文字,例如 Z
、天
、👍
。
然而,在计算机中,字符的表示方式与整数、实数不同。要表示字符,首先需要确定可表示的字符范围,并为该范围内的每个字符分配一个唯一的数字。在 Unicode 标准中,这个唯一的数字被称为“码位”(Code Point)。
尽管在大多数情况下,码位与字符是一一对应的,但仍存在许多特殊情况。
例如,à
和 à
在视觉上是相同的,编辑时也感觉是单个字符,但前者只占用一个码位(U+00E0),而后者是普通拉丁字母 a
(U+0061)与组合用重音符 ◌̀
(U+0300)的序列,占用两个码位。日常生活中常见的 emoji 也有许多需要多个码位才能表示。
因此,现代编程语言通常会避免使用可能存在歧义的“字符”概念,转而使用“码位”或特定编码的“编码单元”作为描述字符串相关概念的最基本单位。
Unicode 码位字面量
Zig 中没有专门的字符类型。与之最接近的是 Unicode 码位字面量。
使用单引号将文字包围,即可创建 Unicode 码位字面量。这是一种 comptime_int
类型的值,表示单个 Unicode 码位。
// 格式化时,可以使用 u 输出对应的字符
const me_zh = '我';
print("{0u} = {0x}\n", .{me_zh}); // 我 = 6211
// 如果是 ASCII 字符,还可以使用 c 进行格式化
const me_en = 'I';
print("{0u} = {0c} = {0x}\n", .{me_en}); // I = I = 49
// 下面的写法会报错,因为这些 emoji 虽然看上去只有一个字,但其实需要由多个码位组合而成
// const hand = '🖐🏽';
// const flag = '🇨🇳';
字符串字面量
Zig 中的字符串字面量是一个单项指针,它指向将字符串进行 UTF-8 编码后得到的字节数组。这个字节数组与 C 语言中的字符串类似,都是以 NUL 字符结尾的。因此,字符串字面量既可以隐式转换为 u8
切片,也可以隐式转换为以 0 结尾的指针。
// 存储的是 UTF-8 编码序列
const bytes = "Hello, 世界!";
print("{}\n", .{@TypeOf(bytes)}); // *const [16:0]u8
print("{}\n", .{bytes.len}); // 16
// 通过索引访问到的是 UTF-8 编码序列中的字节
// 由于 UTF-8 兼容 ASCII,所以可以直接打印 ASCII 字符
print("{c}\n", .{bytes[1]}); // 'e'
// “世”字的 UTF-8 编码为 E4 B8 96
try expect(bytes[7] == 0xE4);
try expect(bytes[8] == 0xB8);
try expect(bytes[9] == 0x96);
// 以 NUL 结尾
print("{d}\n", .{bytes[16]}); // 0
与 C 语言不同,Zig 中的字符串字面量与 Unicode 码位字面量之间没有直接关系。对字符串字面量进行索引操作,得到的是编码序列中的单个字节,而非 Unicode 码位。然而,由于 UTF-8 编码兼容 ASCII,对于 ASCII 字符而言,两者的数值恰好相等。
正确处理 UTF-8 字符串是一项复杂的工作。标准库在 std.unicode
包中提供了用于操作 Unicode 码位和 UTF-8 编码序列的工具。
🅿️ 只能是 UTF-8 编码吗?
并非如此。字符串字面量可以借助 \xNN
转义序列来存放任意形式的字节数据。
但由于 Zig 源文件使用 UTF-8 编码,并且 \u{NNNNNN}
转义序列固定生成码位对应的 UTF-8 编码序列,因此字符串字面量在正常情况下存放的都是 UTF-8 数据。
多行字符串字面量以 \\
开头,不会执行任何转义,并且不包含最后一行的换行符。
// “我”字的 UTF-8 编码为 E6 88 91
const string =
\\I
\\我
;
try expect(string[0] == 'I');
try expect(string[1] == '\n');
try expect(string[2] == 0xE6);
try expect(string[3] == 0x88);
try expect(string[4] == 0x91);
try expect(string[5] == 0);
try expect(string.len == 5);
C 中的字符类型
Zig 还提供了 c_char
类型,它对应 C 语言中的 char
类型。该类型主要用于与 C 语言的交互,在其他情况下不建议使用。
u8
和 c_char
u8
和 c_char
并非完全等价。尽管 c_char
也是 8 位,但其是否有符号取决于目标机器(target)。
布尔值
常用于流程控制
在 Zig 中,布尔值只有 true
和 false
两个。它们在内存中占用 1 个字节。