Parsing Erlang:在二进制流中解组可变长度的数据字段

Parsing Erlang:在二进制流中解组可变长度的数据字段,parsing,binary,erlang,Parsing,Binary,Erlang,我正在创建一个Erlang应用程序,它需要解析来自第三方程序的二进制TCP流。 我可以接收的数据包类型之一的数据格式如下: N_术语*[标志(8位),类型(8位),[可选数据]] 我的问题是,可选数据是由所有可能数据的排列决定的 标志和类型的组合。此外,根据类型,还有与之关联的其他可选数据 如果我要用命令式语言编写解析器,我只需读取2个字段,然后有一系列If(…)语句,在这些语句中,我将读取一个值并增加我在流中的位置。在Erlang中,我最初天真的假设是,我将有2^N个函数子句来匹配流上的字节语

我正在创建一个Erlang应用程序,它需要解析来自第三方程序的二进制TCP流。 我可以接收的数据包类型之一的数据格式如下:

N_术语*[标志(8位),类型(8位),[可选数据]]

我的问题是,可选数据是由所有可能数据的排列决定的 标志和类型的组合。此外,根据类型,还有与之关联的其他可选数据

如果我要用命令式语言编写解析器,我只需读取2个字段,然后有一系列If(…)语句,在这些语句中,我将读取一个值并增加我在流中的位置。在Erlang中,我最初天真的假设是,我将有2^N个函数子句来匹配流上的字节语法,其中N是标志总数+所有类型以及其他可选数据

目前,我至少有3个标志和1个类型,其中包含我必须实现的可选数据,这意味着我将有16个不同的函数子句来匹配流

在Erlang中,必须有一种更好的、惯用的方法来实现这一点——我遗漏了什么

编辑:
我应该事先澄清我不知道术语的数量。

一个解决方案是

 <<Flag:8/integer, Type:8/integer, Rest/binary>>

接下来是您正在搜索的引擎盖下移动的指针。因此,您可以将二进制文件分解成这样的片段,以获得下一个要处理的部分。

我将对其进行如下解析:

parse_term(Acc,0,<<>>) ->  {ok,Acc};
parse_term(_,0,_)      ->  {error,garbage};
parse_term(Acc,N,<<Flag:8/integer,Type:8/integer,Rest/binary>>) ->
 {Optional,Rest1} = extract_optional(Flag,Type,Rest),   
 parse_term([{Flag,Type,Optional}|Acc],N-1,Rest1>>).

parse_stream(<<NTerms/integer,Rest/binary>>)->
    parse_term([],NTerms,Rest).
parse_项(Acc,0,)->{ok,Acc};
解析_项(0,0,0)->{error,garbage};
解析_项(Acc,N,)->
{Optional,Rest1}=extract_Optional(标志,类型,Rest),
parse_term([{Flag,Type,Optional}|Acc],N-1,Rest1>>)。
解析_流()->
解析_项([],entrems,Rest)。

因此,在我的主解析器(尾部递归函数)中,我将有一个嵌套的解码调用(…),它可能会返回一个术语和长度的元组,我将使用该长度传递“Rest”。。。正当那么,我是否需要将16个声明转换为decode()?decode返回一个proplist,其中列出了{Flag,Type}对的有效解码。然后将Proplist传递给另一个解码器函数:decode_有效载荷(Proplist,Rest),该函数可以根据Proplist中给出的描述对Rest进行解码。这意味着只有K个案例,Rest可能拥有的每种属性只有一次,并且这些案例的描述是在decode(标志,类型)中形成的。我接受了你的建议,我意识到这提醒了我延迟过程调用的列表(从上到下半部分调用驱动程序)。这让我想到,如果你有一个函数列表,并将它们传递到列表中:foldl使用累加器字段来保存一个移动的“Rest”目标呢?结果证明,这很有效,而且它使我不必翻译道具列表,我就可以直接了解事情的实质。不过,谢谢你把我的车开到那个方向!是的,这是一个更好的解决方案。小巧、简洁、快速。
parse_term(Acc,0,<<>>) ->  {ok,Acc};
parse_term(_,0,_)      ->  {error,garbage};
parse_term(Acc,N,<<Flag:8/integer,Type:8/integer,Rest/binary>>) ->
 {Optional,Rest1} = extract_optional(Flag,Type,Rest),   
 parse_term([{Flag,Type,Optional}|Acc],N-1,Rest1>>).

parse_stream(<<NTerms/integer,Rest/binary>>)->
    parse_term([],NTerms,Rest).