檢視原始碼 位元語法

簡介

位元語法的完整規範出現在參考手冊中。

在 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.