4 位语法
4.1 简介
位语法的完整规范见参考手册。
在 Erlang 中,Bin 用于构造二进制和匹配二进制模式。Bin 以以下语法编写
<<E1, E2, ... En>>
Bin 是一个低级位的或字节的序列。Bin 的目的是允许构造二进制
Bin = <<E1, E2, ... En>>
所有元素必须绑定。或匹配一个二进制
<<E1, E2, ... En>> = Bin
这里,Bin 被绑定,元素被绑定或未绑定,就像在任何匹配中一样。
Bin 不需要由完整的字节数组成。
**位串** 是一个包含零个或多个位的序列,其中位数不需要是 8 的倍数。如果位数是 8 的倍数,那么位串也是一个二进制。
每个元素指定位串的特定**段**。段是二进制中一组连续的位(不一定是字节边界)。第一个元素指定初始段,第二个元素指定后面的段,依此类推。
以下示例说明了如何构造或匹配二进制,以及如何指定元素和尾部。
示例
**示例 1:** 二进制可以从一组常量或字符串文字构造
Bin11 = <<1, 17, 42>>, Bin12 = <<"abc">>
这将产生两个大小为 3 的二进制,其评估结果如下
- binary_to_list(Bin11) 评估为 [1, 17, 42]。
- binary_to_list(Bin12) 评估为 [97, 98, 99]。
**示例 2:** 同样,二进制可以从一组绑定变量构造
A = 1, B = 17, C = 42, Bin2 = <<A, B, C:16>>
这将产生一个大小为 4 的二进制。这里,**大小表达式** 用于变量 C 来指定 Bin2 的一个 16 位段。
binary_to_list(Bin2) 评估为 [1, 17, 00, 42]。
**示例 3:** Bin 也可以用于匹配。 D、E 和 F 是未绑定变量,Bin2 被绑定,如示例 2 所示
<<D:16, E, F/binary>> = Bin2
这将得到 D = 273、E = 00,F 绑定到一个大小为 1 的二进制:binary_to_list(F) = [42]。
**示例 4:** 以下是一个更复杂的匹配示例。这里,Dgram 绑定到 IP 协议版本 4 的 IP 数据报的连续字节。目标是提取数据报的报头和数据
-define(IP_VERSION, 4). -define(IP_MIN_HDR_LEN, 5). DgramSize = byte_size(Dgram), case Dgram of <<?IP_VERSION:4, HLen:4, SrvcType:8, TotLen:16, ID:16, Flgs:3, FragOff:13, TTL:8, Proto:8, HdrChkSum:16, SrcIP:32, DestIP:32, RestDgram/binary>> when HLen>=5, 4*HLen=<DgramSize -> OptsLen = 4*(HLen - ?IP_MIN_HDR_LEN), <<Opts:OptsLen/binary,Data/binary>> = RestDgram, ... end.
这里,对应于 Opts 变量的段有一个**类型修饰符**,指定 Opts 应该绑定到一个二进制。所有其他变量的默认类型都等于无符号整数。
IP 数据报报头是可变长度的。此长度以 32 位字的数量来衡量,并在对应于 HLen 的段中给出。HLen 的最小值为 5。它是对应于 Opts 的段是可变的,因此如果 HLen 等于 5,则 Opts 将成为一个空二进制。
尾部变量 RestDgram 和 Data 绑定到二进制,就像所有尾部变量一样。两者都可以绑定到空二进制。
如果发生以下情况之一,则 Dgram 的匹配将失败
- Dgram 的前 4 位段不等于 4。
- HLen 小于 5。
- Dgram 的大小小于 4*HLen。
4.2 词法说明
请注意,"B=<<1>>" 将被解释为 "B =< <1>>",这是一个语法错误。编写表达式的正确方法是:B = <<1>>。
4.3 段
每个段都具有以下一般语法
值:大小/类型说明符列表
大小 或 类型说明符,或两者都可以省略。因此,允许以下变体
- 值
- 值:大小
- 值/类型说明符列表
当规格缺失时,使用默认值。默认值在默认值中描述。
在二进制构造中使用时,值 部分是任何表达式。在二进制匹配中使用时,值 部分必须是文字或变量。有关 值 部分的更多信息,请参阅构造二进制和位串 和 匹配二进制。
段的 大小 部分乘以 类型说明符列表 中的单位(稍后描述)给出段的位数。在构造中,大小 是任何评估为整数的表达式。在匹配中,大小 必须是常量表达式或变量。
类型说明符列表 是一个由连字符分隔的类型说明符列表。
- 类型
- 最常用的类型是 integer、float 和 binary。有关完整描述,请参阅参考手册中的位语法表达式。
- 符号
- 符号规范可以是 signed 或 unsigned。请注意,符号只在匹配时才重要。
- 字节序
- 字节序规范可以是 big、little 或 native。本机字节序意味着字节序在加载时解析,是 big-endian 或 little-endian,具体取决于 Erlang 机器运行的 CPU 的“本机”是什么。
- 单位
- 单位大小以 unit:IntegerLiteral 给出。允许的范围是 1-256。它乘以 大小 说明符来给出段的有效大小。单位大小指定了没有大小的二进制段的对齐方式。
示例
X:4/little-signed-integer-unit:8
此元素的总大小为 4*8 = 32 位,它包含一个以 little-endian 顺序排列的有符号整数。
4.4 默认值
段的默认类型是 integer。默认类型不依赖于值,即使值是文字。例如,<<3.14>> 中的默认类型是 integer,而不是 float。
默认 大小 取决于类型。对于 integer,它是 8。对于 float,它是 64。对于 binary,它是整个二进制。在匹配中,此默认值仅对最后一个元素有效。匹配中的所有其他二进制元素都必须具有大小规范。
默认单位取决于类型。对于 integer、float 和 bitstring,它是 1。对于 binary,它是 8。
默认符号是 unsigned。
默认字节序是 big。
4.5 构造二进制和位串
本节描述了使用位语法构造二进制的规则。与构造列表或元组不同,构造二进制可能会因 badarg 异常而失败。
要构造的二进制中可以有零个或多个段。表达式 <<>> 构造一个零长度二进制。
二进制中的每个段可以包含零个或多个位。类型为 integer 和 float 的单个段没有对齐规则。对于没有大小的二进制和位串,单位指定对齐方式。由于 binary 类型的默认对齐方式是 8,因此二进制段的大小必须是 8 位的倍数,即只能是完整的字节。
示例
<<Bin/binary,Bitstring/bitstring>>
变量 Bin 必须包含完整的字节数,因为 binary 类型的默认值为 unit:8。如果 Bin 包含例如 17 位,则会生成 badarg 异常。
变量 Bitstring 可以包含任意数量的位,例如 0、1、8、11、17、42 等。这是因为位串的默认 unit 为 1。
为了清晰起见,建议不要更改二进制的单位大小。相反,当您需要字节对齐时使用 binary,当您需要位对齐时使用 bitstring。
以下示例成功构造了一个 7 位的位串,前提是 X 和 Y 都是整数
<<X:1,Y:6>>
如前所述,段具有以下一般语法
值:大小/类型说明符列表
在构造二进制时,值 和 大小 可以是任何 Erlang 表达式。但是,由于语法原因,如果表达式包含的不仅仅是一个文字或一个变量,则 值 和 大小 都必须用括号括起来。以下会产生编译器语法错误
<<X+1:8>>
此表达式必须重写为以下形式,才能被编译器接受
<<(X+1):8>>
包含文字字符串
文字字符串可以代替元素
<<"hello">>
这相当于以下语法糖
<<$h,$e,$l,$l,$o>>
4.6 匹配二进制
本节描述了使用位语法匹配二进制的规则。
二进制模式中可以有零个或多个段。二进制模式可以出现在允许模式的任何地方,包括其他模式内部。二进制模式不能嵌套。模式 <<>> 匹配一个零长度二进制。
二进制中的每个段可以包含零个或多个位。类型为 binary 的段的大小必须是 8 的倍数(或者可被单位大小整除,如果单位大小已更改)。类型为 bitstring 的段对大小没有限制。类型为 float 的段的大小必须是 64 或 32。
如前所述,段具有以下一般语法
值:大小/类型说明符列表
匹配 值 时,值必须是变量、整数或浮点文字。表达式不允许。
大小 必须是 保护表达式,它可以使用文字和以前绑定的变量。以下不允许
foo(N, <<X:N,T/binary>>) -> {X,T}.
两次出现 N 彼此无关。编译器会抱怨大小字段中的 N 未绑定。
编写此示例的正确方法如下
foo(N, Bin) -> <<X:N,T/binary>> = Bin, {X,T}.
在 OTP 23 之前,大小 被限制为一个整数或一个绑定到整数的变量。
绑定和使用大小变量
有一个例外是,作为大小使用的变量必须事先绑定。可以在同一个二进制模式中匹配和绑定一个变量,并将其用作大小。例如
bar(<<Sz:8,Payload:Sz/binary-unit:8,Rest/binary>>) -> {Payload,Rest}.
这里 Sz 被绑定到二进制的第一字节的值。 Sz 然后被用作匹配出的二进制字节数。
从 OTP 23 开始,大小可以是一个保护表达式。
bar(<<Sz:8,Payload:((Sz-1)*8)/binary,Rest/binary>>) -> {Payload,Rest}.
这里 Sz 是报头和有效负载的组合大小,所以我们需要减去一个字节才能得到有效负载的大小。
获取二进制或位串的其余部分
要匹配出二进制的其余部分,请指定一个没有大小的二进制字段。
foo(<<A:8,Rest/binary>>) ->
尾部的尺寸必须能被 8 整除。
要匹配出位串的其余部分,请指定一个没有大小的字段。
foo(<<A:8,Rest/bitstring>>) ->
尾部中的位数没有限制。
4.7 追加到二进制
可以如下高效地将数据追加到二进制。
triples_to_bin(T) -> triples_to_bin(T, <<>>). triples_to_bin([{X,Y,Z} | T], Acc) -> triples_to_bin(T, <<Acc/binary,X:32,Y:32,Z:32>>); triples_to_bin([], Acc) -> Acc.