如何在Erlang中连接两个二进制文件?

55

如何在Erlang中连接两个二进制文件?

例如,假设我有:

B1 = <<1,2>>.
B2 = <<3,4>>.

如何将B1和B2连接起来生成一个二进制B3,并使其成为<<1,2,3,4>>?

我提出这个问题是因为我正在编写用于某些网络协议的数据包编码代码。我通过对数据包中各字段编写编码器来实现此目的,我需要将这些字段连接起来以构建整个数据包。

也许我做法有误。我应该将数据包构建为整数列表,然后在最后一刻将该列表转换为二进制吗?

5个回答

131
28> B1= <<1,2>>.
<<1,2>>
29> B2= <<3,4>>.
<<3,4>>
30> B3= <<B1/binary, B2/binary>>.
<<1,2,3,4>>
31>

大概这不是一个O(1)操作,所以按照cthulahoops的建议构建一个深列表(IO列表)仍然是有意义的,并且推迟遍历深列表直到我准备好发送数据包。 - Bruno Rijsman
18
两个答案都很好。来自cthulahoops的io_list结构非常适合输入/输出;但是,既然现在它是“erlang连接二进制文件”的谷歌热门搜索结果,有一个严格正确的答案也很不错。 - JasonSmith

41
您可以使用位语法连接二进制数据:
1> B1 = <<1,2>>.
<<1,2>>
2> B2 = <<3,4>>.
<<3,4>>
3> B3 = <<B1/binary, B2/binary>>.
<<1,2,3,4>>

在许多情况下,特别是当数据将发送到网络时,可以通过构造io_list来避免连接操作。

B3 = [B1, B2],
gen_tcp:send(Socket, B3).

使用这种方法是O(1)的,因为它避免了对任何二进制数据的复制。 gen_tcp:send 函数可接受深度嵌套的列表,并在输出时对其进行遍历。 (一个两个元素的列表只需要很少的额外内存,因此内存开销很小。)

在某些情况下(向同一二进制数据进行重复添加),Erlang 现在具有优化功能,可以避免复制要追加的二进制数据,因此使用 io_lists 可能就不那么相关了:http://erlang.org/doc/efficiency_guide/binaryhandling.html#constructing-binaries


历史注:我最初只建议使用 io_list 解决方案,许多评论者正确指出我未能回答问题。希望现在被接受的答案已经完整了。(11年后!)


25
我不认为这段代码是正确的。它返回了[<<1,2>>,<<3,4>>],而这不是 @Cayle Spandon 所要求的。所以答案是错误的。 - Worker
5
@Worker - cthulahoops的回答并非旨在实际连接这两个二进制文件。这个答案的重点是,如果您正在使用的代码可以接受iolists,则可以避免首先连接两个二进制文件的计算成本。由于原始提问者说他们正在尝试构建一个网络数据包,因此这个答案表明,也许连接这两个二进制文件并不是实现原始提问者需求的最佳方式。 - Nick
2
@Nick 这个回答并没有回答[主标题]的问题,因此是无效的。我来这里是因为我需要在Erlang中连接两个二进制文件以解决一个完全不同的问题。这也是OP的错,因为他在一个问题中提出了两个不同的问题,但这是谷歌上针对“如何在Erlang中连接两个二进制文件”的通用问题的最佳答案,而且它并没有回答那个问题。 - Tommy
OP也是一个粗心的SO用户,他接受了题为“如何在Erlang中连接两个二进制文件”的问题的答案,并回答“不要连接”。这会影响未来的搜索者。 - Tommy
根据 效率指南 在 R12B 版本之后(大约是问这个问题的时候)位串连接不会带来如此大的惩罚;多次向“相同”的二进制附加可以比创建iolists更有效地利用空间。 - bottlenecked
显示剩余2条评论

22

要使用 io_list,您可以执行以下操作:

erlang:iolist_to_binary([<<"foo">>, <<"bar">>])

这看起来非常清晰易读。如果更方便的话,您还可以在其中使用列表和其他元素。


13

在上一个答案的基础上进行构建:

bjoin(List) ->
    F = fun(A, B) -> <<A/binary, B/binary>> end,
    lists:foldr(F, <<>>, List).

8

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接