字符与布尔值
计算机中的字符并非我们平常所指的单个文字。特定的编码方案首先需要将单个文字对应到若干个码位,存储码位时还需要转换为字节序列。
布尔值往往通过二进制的 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个字节。