查看源码 位语法
简介
位语法的完整规范在参考手册中给出。
在 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
。
词法注释
请注意,"B=<<1>>
" 将被解释为 "B = < <1>>
",这是一个语法错误。编写该表达式的正确方法是:B = <<1>>
。
段
每个段具有以下通用语法
Value:Size/TypeSpecifierList
可以省略 Size
或 TypeSpecifier
,或两者都省略。因此,允许以下变体
Value
Value:Size
Value/TypeSpecifierList
当规范缺失时,将使用默认值。默认值在默认值中描述。
当用于二进制构造时,Value
部分是任何表达式。当用于二进制匹配时,Value
部分必须是文字或变量。有关 Value
部分的更多信息,请参阅构造二进制数据和位串和匹配二进制数据。
段的 Size
部分乘以 TypeSpecifierList
中的单位(稍后描述)给出段的位数。在构造中,Size
是任何求值为整数的表达式。在匹配中,Size
必须是常量表达式或变量。
TypeSpecifierList
是一个由连字符分隔的类型说明符列表。
类型 - 最常用的类型是
integer
、float
和binary
。有关完整描述,请参阅参考手册中的位语法表达式。有符号性 - 有符号性规范可以是
signed
或unsigned
。请注意,有符号性仅对匹配有意义。字节序 - 字节序规范可以是
big
、little
或native
。原生字节序意味着字节序在加载时解析,根据 Erlang 机器运行的 CPU 的“原生”字节序,解析为大端或小端。单位 - 单位大小以
unit:IntegerLiteral
给出。允许的范围是 1-256。它乘以Size
说明符,以给出段的有效大小。单位大小指定没有大小的二进制段的对齐方式。
示例
X:4/little-signed-integer-unit:8
此元素的总大小为 4*8 = 32 位,它包含一个小端顺序的有符号整数。
默认值
段的默认类型是整数。默认类型不依赖于值,即使该值是文字。例如,<<3.14>>
中的默认类型是整数,而不是浮点数。
默认的 Size
取决于类型。对于整数,它是 8。对于浮点数,它是 64。对于二进制数据,它是整个二进制数据。在匹配中,此默认值仅对最后一个元素有效。匹配中的所有其他二进制元素都必须具有大小规范。
默认单位取决于类型。对于 integer
、float
和 bitstring
,它是 1。对于二进制数据,它是 8。
默认的有符号性是 unsigned
。
默认的字节序是 big
。
构造二进制数据和位串
本节描述了使用位语法构造二进制数据的规则。与构造列表或元组不同,二进制数据的构造可能会因 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>>
如前所述,段具有以下通用语法
Value:Size/TypeSpecifierList
构造二进制数据时,Value
和 Size
可以是任何 Erlang 表达式。但是,出于语法原因,如果表达式包含单个文字或变量以外的任何内容,则 Value
和 Size
都必须用括号括起来。以下给出了编译器语法错误
<<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.