erlang 二进制与位语法

原文 2013-09-11 20:28:16 发表于 CSDN,这里对以前写的文章做下收录。

Erlang的位语法(Erlang Bit Syntax)提供了一种方法来表示和匹配二进制数据,这使得Erlang二进制对象在某种程度上等同于其他Erlang对象,如元组和列表。也因为其快速高效,使得在Erlang中,二进制对象被广泛使用,尤其是在协议编程方面。(官方文档

位语法/比特语法(bit syntax)
Erlang的位语法表达式,即Erlang二进制的表示形式:
<<>>
<<E1,...,En>>

每个元素Ei指定了一段二进制串(bit string)。每个元素Ei都是一个值,后面可以带有可选参数Size、TypeSpecifierList,如下:
Ei = Value |
Value:Size |
Value/TypeSpecifierList |
Value:Size/TypeSpecifierList

Size表示前一个Value数据存储的位数,默认是8位,也就是一个字节。
TypeSpecifierList可以是以下几种类型及其组合,组合以 - 相连
Type = integer | float | binary | bytes |bitstring | bits | utf8 | utf16 | utf32
       默认值是integer。bytes是binary的简写,bits是bitsring的简写
Signedness = signed | unsigned
       只有当type为integer时有用,默认是unsigned
Endianness = big | little | native
       当type为integer,utf16,utf32,float有用,默认是big
Unit = unit:IntegerLiteral
       有效范围是1-256,integer、float和bitstring默认是1,binary默认是8
例如:
-define( UINT, 32/unsigned-little-integer).
-define( INT, 32/signed-little-integer).
-define( USHORT, 16/unsigned-little-integer).
-define( SHORT, 16/signed-little-integer).
-define( UBYTE, 8/unsigned-little-integer).
-define( BYTE, 8/signed-little-integer).
-define( CHAR, 1/binary-unit:8).

不同的bit syntax生成的二进制是不同的,如下:

2> <<111:16/signed-big-integer>>.
<<0,111>>
3> <<111:16/signed-little-integer>>.
<<111,0>>

下面以官方文档的一段代码,辅助理解下:

1> Bin1 = <<1,17,42>>.
<<1,17,42>>
2> Bin2 = <<"abc">>.
<<97,98,99>>
3> Bin3 = <<1,17,42:16>>.
<<1,17,0,42>>
4> <<A,B,C:16>> = <<1,17,42:16>>.
<<1,17,0,42>>
5> C.
42
6> <<D:16,E,F>> = <<1,17,42:16>>.
<<1,17,0,42>>
7> D.
273
8> F.
42
9> <<G,H/binary>> = <<1,17,42:16>>.
<<1,17,0,42>>
10> H.
<<17,0,42>>
11> <<G,H/bitstring>> = <<1,17,42:12>>.
<<1,17,2,10:4>>
12> H.
<<17,2,10:4>>

例子说明:
1、例子1和2:从一组常量或一个字符串来构造二进制对象

1> Bin1 = <<1,17,42>>.
<<1,17,42>>
2> Bin2 = <<"abc">>.
<<97,98,99>>

以上表示的二进制大小都是3字节(1+1+1)。
字符串"abc"转化成latin1码也就是 97,98,99,然后二进制默认用8位记录,所以结果就是<<97,98,99>>。
2、例子3:从一组限定边界的变量来构造二进制对象

3> Bin3 = <<1,17,42:16>>.
<<1,17,0,42>>

以上表示的二进制大小为4字节(1+1+2)。
上面,我们给42指定了16位(2个字节)的大小来记录,所以在内存中的数据为0000 0000 0010 1010,而Erlang二进制的默认表示,以1个字节(8位)为单位分割数据。
所以<<42:16>> 构成的二进制也就是<<0:8, 42:8>>  ,然后,8是默认值可以不显示,也就是<<0, 42>>
3、例子4、6:按照某种形式匹配二进制对象

4> <<A,B,C:16>> = <<1,17,42:16>>.
<<1,17,0,42>>
6> <<D:16,E,F>> = <<1,17,42:16>>.
<<1,17,0,42>>

例子4很好理解,这里说一下例子6吧,为何D会得到273的结果?
<<1,17,42:16>>生成的二进制数据前面说过了,为<<1,17,0,42>>
D:16表示匹配<<1,17,42:16>>前面16位的数据 <<1:8, 17:8>>,所以得到就是<<1,17>>
然后, <<1,17>>在内存中表示为0000 0001 0001 0001,结果就是256 + 16 + 1,也就是273,这就得到了D的值
4、例子11:也是从一组限定边界的变量来构造二进制对象,和例子3不同的是大小不是8的倍数。
<<1,17,42:12>>.
上面,我们给42指定了12位的大小来存储,所以在内存中的数据为0000 0010 1010,
这样构造出来的对象就是<<2,10:4>>
在erlang 二进制中,对象默认以8位为一个单位来表示,不足就向后面借位,比如:<<42:12,1,17>>生成的二进制对象是<<2,160,17,1:4>>,其实两者表达的是一段相同的二进制数据。
注意:
 "B=<<1>>" 在erlang中被解释成 "B =< <1>>",这会引起一个语法错误“syntax error before: '<'”。正确的做法是写成 "B = <<1>>" 

binary与bitstring的区别?
erlang文档有说明:
A bitstring is a sequence of zero or more bits, where the number of bits does not need to be divisible by 8. If the number of bits is divisible by 8, the bitstring is also a binary.
就是说,binary也是bitstring的一种,当二进制串大小能被8整除,就是binary
什么情况下会区分?
如模式匹配:

foo(<<A:8,Rest/binary>>) ->
	{binary, A, Rest};
foo(<<A:8,Rest/bitstring>>) ->
	{bitstring, A, Rest}.

2016/3/7 补充对二进制的解释
2015/7/17 补充binary与bitstring的区别

发表评论

邮箱地址不会被公开。 必填项已用*标注