檢視原始碼 位元語法
簡介
位元語法的完整規範出現在參考手冊中。
在 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.