处理二进制数据可能会很麻烦,因此 Gleam 提供了 BitArray 类型以及配套语法,用来构造二进制数据,以及对它进行模式匹配。
位数组字面量使用<<>>语法定义。定义位数组字面量时,它由若干段组成。每一段都包含一个值和一段注解,两者之间用:分隔。注解指定用多少位来编码这个值,也可以完全省略,省略时默认为 8 位整数值。
// This defines a bit array with three segments of a single bit each
<<0:1, 1:1, 0:1>>
// This defines a bit array with three segments of 8 bits each
<<0, 1, 0>>
把类型写成:1是:size(1)的简写。如果位大小来自变量,就需要使用更长的写法。
let segment_size = 1
<<0:size(segment_size), 1:size(segment_size), 0:size(segment_size)>>
编写二进制整数字面量时,我们可以在字面量前面加上0b,直接使用二进制记法。注意,在测试里或在你的程序中打印它们时,显示出来的是十进制数字。
<<0b1011:4>> == <<11:4>>
// -> True
如果某一段的值超出了该段类型能容纳的范围,就会从左侧截断。
<<0b1011:3>> == <<0b0011:3>>
// -> True
使用位数组语法,你既可以在现有位数组的前面添加内容,也可以在其后面追加内容。对于已有的位数组,必须使用:bits注解。
let value = <<0b110:3, 0b001:3>>
let new_value = <<0b011:3, value:bits, 0b000:3>>
// -> <<120, 8:size(4)>>
使用这种语法,我们可以拼接保存在变量里的位数组。拼接两个大小可变的位数组时,必须使用:bits注解。
let first = <<0b110:3>>
let second = <<0b001:3>>
let concatenated = <<first:bits, second:bits>>
// -> <<49:size(6)>>
也可以用模式匹配从位数组中取值。对于想捕获的每一个片段,你必须知道它的位数,但有一个例外::bits注解可用于对大小未知的位数组进行模式匹配,不过它只能用在最后一个片段上。
let assert <<value:4, rest:bits>> = <<0b01101001:8>>
value == 0b0110
// -> true
位数组打印出来的格式可能和创建它们时使用的格式不同。这在学习位数组时常常会造成困惑。
默认情况下,位数组以 8 位(一个字节)为片段显示,即使你创建它们时用的片段大小不同也是如此。
<<2011:11>>
// -> <<251, 3:size(3)>>
如果你创建的位数组表示一个可打印的 UTF-8 编码字符串,那么在以 Erlang 为目标平台时,使用echo keyword就可能把它显示为字符串。这是 Gleam 内部表示字符串方式的一个实现细节造成的。
<<>>
// -> ""
<<65, 66, 67>>
// -> "ABC"