文章目录

Erlang 二进制数据:<<>> 语法

发布于 2026-04-03 18:01:54 · 浏览 3 次 · 评论 0 条

Erlang 二进制数据:<<>> 语法

Erlang 使用 <<>> 语法创建和操作二进制数据。这种语法让你能精确控制字节、位字段和数据编码,是处理网络协议、文件格式或加密数据的核心工具。


创建基本二进制

  1. 输入 <<1, 2, 3>> 创建一个包含三个字节的二进制。每个数字默认占 8 位(1 字节),值必须在 0255 范围内。
  2. 输入 <<256>> 会报错,因为 256 超出单字节范围。若需更大数值,需显式指定位宽。
  3. 使用 <<1000:32>> 创建一个 32 位(4 字节)整数。冒号后数字表示位数,值按大端序(big-endian)存储。

指定位顺序和符号

Erlang 默认使用大端序(高位在前)、无符号、整数类型。可通过修饰符覆盖这些默认行为。

  1. 输入 <<100:16/little>> 创建小端序的 16 位整数/little 表示低位在前。
  2. 输入 <<-5:8/signed>> 创建带符号的 8 位整数/signed 允许负值,范围变为 -128127
  3. 组合多个修饰符<<100:32/little-signed-integer>> 明确指定小端、有符号、整数类型(尽管 integer 是默认类型,可省略)。

常用修饰符包括:

  • 端序:big(默认)、little
  • 符号:signedunsigned(默认)
  • 类型:integer(默认)、floatbinarybytes(等同于 binary

提取二进制内容

<<>> 语法从已有二进制中解构数据。

  1. 假设 有变量 Bin = <<1, 2, 3, 4>>
  2. 执行 <<A, B, Rest/binary>> = Bin 将前两字节赋给 AB剩余部分作为二进制赋给 Rest。结果:A=1, B=2, Rest=<<3,4>>
  3. 提取固定位宽字段<<Flag:1, Value:15>> = <<16#4000:16>>。这里 Flag 取最高 1 位,Value 取后续 15 位。

注意:模式匹配时,未指定类型的字段默认为 integer,未指定大小的整数默认 8 位,二进制默认匹配到末尾。


处理字符串与二进制

Erlang 字符串本质是整数列表,而二进制是原始字节序列。

  1. 输入 <<"hello">> 创建 UTF-8 编码的二进制。每个字符转为对应字节,如 <<"h">> 等价于 <<104>>
  2. 转换字符串为二进制:使用 list_to_binary("hello") 得到 <<"hello">>
  3. 拼接二进制<<Bin1/binary, Bin2/binary>>。例如 <<<<"a">>/binary, <<"b">>/binary>> 结果为 <<"ab">>

若需处理非 UTF-8 数据(如 Latin-1),直接使用字节值:<<195,164>> 表示 UTF-8 的 "ä",而 <<228>> 是 Latin-1 的 "ä"。


高级位级操作

<<>> 支持任意位宽,不限于 8 的倍数。

  1. 创建 10 位字段<<1023:10>>。值 1023 正好填满 10 位($2^{10} - 1 = 1023$)。
  2. 打包多个小字段<<Version:4, Type:4, Length:16>> = <<16#4500:32>>。这常用于解析 IP 包头:前 4 位版本号(4),接着 4 位头部长度(5),后 16 位总长度(0x00)。
  3. 对齐到字节边界:若总位数不是 8 的倍数,Erlang 自动在末尾补零。例如 <<1:1, 1:1>> 实际生成 <<192>>(二进制 11000000)。

常见错误与规避

错误场景 正确做法
值超出位宽范围 计算最大值:n 位无符号整数最大为 $2^n - 1$,有符号为 $2^{n-1} - 1$
模式匹配长度不匹配 确保左侧总位数等于右侧二进制长度(字节对齐时注意补零)
忘记 /binary 导致崩溃 提取剩余数据时务必写 Rest/binary,否则默认按 8 位整数匹配

实战:解析简单协议

假设协议格式:1 字节类型 + 2 字节长度 + N 字节数据。

  1. 接收数据 Packet = <<1, 0, 5, "hello">>
  2. 执行 <<Type, Len:16, Data:Len/binary>> = Packet
  3. 得到 Type=1, Len=5, Data=<<"hello">>

此模式自动根据 Len 值决定 Data 长度,无需手动切片。


构建动态二进制

在运行时根据变量生成二进制。

  1. 定义变量 Size = 100, Checksum = 42
  2. 构建 Header = <<Size:32, Checksum:8>>
  3. 拼接完整包Packet = <<Header/binary, Payload/binary>>

注意:变量在 <<>> 中直接使用,无需额外语法。


性能提示

  • 避免频繁拼接<<A/binary, B/binary>> 在循环中会产生大量中间二进制。改用 iolist(如 [A, B])更高效,最后用 iolist_to_binary/1 转换。
  • 预分配大小:若已知最终大小,一次性构建比逐步追加更快。
  • 位语法编译优化:Erlang 编译器对 <<>> 模式匹配高度优化,优先使用它而非 binary:part/3 等函数。

浮点数处理

浮点数默认以 IEEE 754 双精度(64 位)存储。

  1. 创建浮点二进制<<3.14/float>>
  2. 指定精度<<3.14:32/float>> 使用单精度(32 位)。
  3. 混合整数与浮点<<100, 3.14/float>> 生成 1 字节整数 + 8 字节浮点。

注意:浮点数不能指定端序修饰符(如 /little),因其内部结构固定。


二进制推导式

类似列表推导式,但生成二进制。

  1. 转换列表为二进制<< <<X>> || X <- [1,2,3] >> 结果为 <<1,2,3>>
  2. 处理字符串<< <<C/utf8>> || C <- "åäö" >> 生成 UTF-8 编码二进制。
  3. 过滤与变换<< <<X*2>> || X <- [1,2,3], X > 1 >> 输出 <<4,6>>

推导式中的表达式必须返回二进制片段(如 <<...>>),不能是裸整数。


调试技巧

  1. 打印二进制内容:使用 io:format("~w~n", [Bin]) 显示为 <<...>> 格式。
  2. 查看十六进制io:format("~16.16.0B~n", [X]) 打印单字节为两位十六进制。
  3. 检查长度byte_size(Bin) 返回字节数,bit_size(Bin) 返回总位数(可能非 8 倍数)。

文件与网络 I/O

Erlang 文件和 socket 默认以二进制模式读写。

  1. 读取文件{ok, Bin} = file:read_file("data.bin")
  2. 解析内容:直接对 Bin 使用 <<>> 模式匹配。
  3. 发送数据gen_tcp:send(Socket, <<Cmd:8, Payload/binary>>)

始终假设外部数据是原始二进制,用 <<>> 语法安全解析,避免直接转换为字符串。


位语法速查表

以下修饰符可自由组合,顺序无关:

类别 选项 说明
端序 big 高位在前(默认)
little 低位在前
native 本机字节序
符号 signed 有符号整数
unsigned 无符号整数(默认)
类型 integer 整数(默认)
float 浮点数
binary 二进制子串
bytes binary
bitstring 位串(允许非 8 倍数)

示例:<<Val:24/little-signed-integer>> 表示小端、有符号、24 位整数。


构造 <<1, 2, 3>> 即可开始使用 Erlang 二进制

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文