查看源码 位语法

简介

位语法的完整规范在参考手册中给出。

在 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 的二进制数据,具有以下求值结果

示例 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>>

每个段具有以下通用语法

Value:Size/TypeSpecifierList

可以省略 SizeTypeSpecifier,或两者都省略。因此,允许以下变体

  • Value
  • Value:Size
  • Value/TypeSpecifierList

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

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

段的 Size 部分乘以 TypeSpecifierList 中的单位(稍后描述)给出段的位数。在构造中,Size 是任何求值为整数的表达式。在匹配中,Size 必须是常量表达式或变量。

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

  • 类型 - 最常用的类型是 integerfloatbinary。有关完整描述,请参阅参考手册中的位语法表达式

  • 有符号性 - 有符号性规范可以是 signedunsigned。请注意,有符号性仅对匹配有意义。

  • 字节序 - 字节序规范可以是 biglittlenative。原生字节序意味着字节序在加载时解析,根据 Erlang 机器运行的 CPU 的“原生”字节序,解析为大端或小端。

  • 单位 - 单位大小以 unit:IntegerLiteral 给出。允许的范围是 1-256。它乘以 Size 说明符,以给出段的有效大小。单位大小指定没有大小的二进制段的对齐方式。

示例

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

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

默认值

段的默认类型是整数。默认类型不依赖于值,即使该值是文字。例如,<<3.14>> 中的默认类型是整数,而不是浮点数。

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

默认单位取决于类型。对于 integerfloatbitstring,它是 1。对于二进制数据,它是 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>>

如前所述,段具有以下通用语法

Value:Size/TypeSpecifierList

构造二进制数据时,ValueSize 可以是任何 Erlang 表达式。但是,出于语法原因,如果表达式包含单个文字或变量以外的任何内容,则 ValueSize 都必须用括号括起来。以下给出了编译器语法错误

<<X+1:8>>

必须将此表达式重写为以下形式,才能被编译器接受

<<(X+1):8>>

包含文字字符串

可以编写文字字符串而不是元素

<<"hello">>

这是以下内容的语法糖

<<$h,$e,$l,$l,$o>>

匹配二进制数据

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

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

二进制数据中的每个段可以由零个或多个位组成。binary 类型的段的大小必须可以被 8 整除(或者可以被单位大小整除,如果单位大小已更改)。bitstring 类型的段的大小没有限制。float 类型的段的大小必须为 64 或 32。

如前所述,段具有以下通用语法

Value:Size/TypeSpecifierList

当匹配 Value 时,值必须是变量或整数,或浮点文字。不允许使用表达式。

Size 必须是一个守卫表达式,可以使用文字和先前绑定的变量。以下是不允许的

foo(N, <<X:N,T/binary>>) ->
   {X,T}.

N 的两次出现无关。编译器将抱怨大小字段中的 N 是未绑定的。

编写此示例的正确方法如下

foo(N, Bin) ->
   <<X:N,T/binary>> = Bin,
   {X,T}.

注意

在 OTP 23 之前,Size 被限制为整数或绑定到整数的变量。

绑定和使用大小变量

有一个例外,即用作大小的变量必须是先前绑定的。可以在同一个二进制模式中匹配和绑定一个变量,并将其用作大小。例如

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.