4  位语法

4  位语法

位语法的完整规范见参考手册

在 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 也可以用于匹配。 DEF 是未绑定变量,Bin2 被绑定,如示例 2 所示

<<D:16, E, F/binary>> = Bin2

这将得到 D = 273E = 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 将成为一个空二进制。

尾部变量 RestDgramData 绑定到二进制,就像所有尾部变量一样。两者都可以绑定到空二进制。

如果发生以下情况之一,则 Dgram 的匹配将失败

  • Dgram 的前 4 位段不等于 4。
  • HLen 小于 5。
  • Dgram 的大小小于 4*HLen

请注意,"B=<<1>>" 将被解释为 "B =< <1>>",这是一个语法错误。编写表达式的正确方法是:B = <<1>>

每个段都具有以下一般语法

值:大小/类型说明符列表

大小类型说明符,或两者都可以省略。因此,允许以下变体

  • 值:大小
  • 值/类型说明符列表

当规格缺失时,使用默认值。默认值在默认值中描述。

在二进制构造中使用时, 部分是任何表达式。在二进制匹配中使用时, 部分必须是文字或变量。有关 部分的更多信息,请参阅构造二进制和位串匹配二进制

段的 大小 部分乘以 类型说明符列表 中的单位(稍后描述)给出段的位数。在构造中,大小 是任何评估为整数的表达式。在匹配中,大小 必须是常量表达式或变量。

类型说明符列表 是一个由连字符分隔的类型说明符列表。

最常用的类型是 integerfloatbinary。有关完整描述,请参阅参考手册中的位语法表达式
符号规范可以是 signedunsigned。请注意,符号只在匹配时才重要。
字节序规范可以是 biglittlenative。本机字节序意味着字节序在加载时解析,是 big-endian 或 little-endian,具体取决于 Erlang 机器运行的 CPU 的“本机”是什么。
单位大小以 unit:IntegerLiteral 给出。允许的范围是 1-256。它乘以 大小 说明符来给出段的有效大小。单位大小指定了没有大小的二进制段的对齐方式。

示例

X:4/little-signed-integer-unit:8

此元素的总大小为 4*8 = 32 位,它包含一个以 little-endian 顺序排列的有符号整数。

段的默认类型是 integer。默认类型不依赖于值,即使值是文字。例如,<<3.14>> 中的默认类型是 integer,而不是 float。

默认 大小 取决于类型。对于 integer,它是 8。对于 float,它是 64。对于 binary,它是整个二进制。在匹配中,此默认值仅对最后一个元素有效。匹配中的所有其他二进制元素都必须具有大小规范。

默认单位取决于类型。对于 integerfloatbitstring,它是 1。对于 binary,它是 8。

默认符号是 unsigned

默认字节序是 big

本节描述了使用位语法构造二进制的规则。与构造列表或元组不同,构造二进制可能会因 badarg 异常而失败。

要构造的二进制中可以有零个或多个段。表达式 <<>> 构造一个零长度二进制。

二进制中的每个段可以包含零个或多个位。类型为 integerfloat 的单个段没有对齐规则。对于没有大小的二进制和位串,单位指定对齐方式。由于 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>>

本节描述了使用位语法匹配二进制的规则。

二进制模式中可以有零个或多个段。二进制模式可以出现在允许模式的任何地方,包括其他模式内部。二进制模式不能嵌套。模式 <<>> 匹配一个零长度二进制。

二进制中的每个段可以包含零个或多个位。类型为 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>>) ->

尾部中的位数没有限制。

可以如下高效地将数据追加到二进制。

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.