【问题标题】:Erlang: Convert binary data into a list containing the number of consecutive onesErlang:将二进制数据转换为包含连续数的列表
【发布时间】:2012-10-02 22:13:47
【问题描述】:

%问题
假设我有一个二进制数:
1011011101111011111
每个数字都是一位。

我希望能够将其转换为:
[1、11、111、1111、11111]

...最终进入:
[1、2、3、4、5]

%我试过了
我尝试了 binary:split,但数据总是以比特块编码。我只想处理原始数据(如果可能的话)。

%我要完成的工作
我正在为路由协议设计一个标头。我希望标头包含数据包已经访问过的地址列表。我想如果我给标题本身一个由零分隔的连续的标题组成的标题,我就不必对标题的总长度施加任何限制。标头的标头将通过两个连续的零与标头本身分开。所以如果我有一个有效载荷,上面写着:
>
并且数据已被 alice、bob 和 carl 访问过,则标题为:
>
并且标头的标头是:
(8*5 个) 0 (8*3 个) 0 (8*4 个) 00
假设我们对标头使用了一些 8 位编码。

然后实际的数据包将显示为:
(8*5 个) 0 (8*3 个) 0 (8*4 个) 00 > >

为了破译报头,我将首先找到 00 的第一个实例,然后将 00 之前的所有内容拆分为每个 0。然后我会将结果列表转换为一个列表,其中包含数据包经过的每个地址中的位数到。然后我终于可以从标头中读取地址并检索有效负载。

【问题讨论】:

  • 两个while循环可以做到这一点。外层循环直到数字变为 0,内层循环在 LSB 为 1 时循环,并计算在到达 0 之前发生的次数。
  • 我很确定 Erlang 没有 for/while 循环。此外,我不知道如何在 Erlang 中实际读取各个位。
  • 哎呀——我对erlang一无所知——也许我的评论没有帮助。这就是我在 C 等低级语言中的做法。
  • Erlang 有递归,所以它可以循环,它有除法和模数,所以你可以读取 LSB 并进行位移。
  • 不应该是(8*5 ones) 0 (8*3 ones) 0 (8*4 ones)吗?

标签: binary erlang


【解决方案1】:

Bitstring comprehensions 回复:

1> Inp = <<1:1,0:1,3:2,0:1,7:3>>.
<<"À">>
2> [ size(B) || B <- binary:split(<< <<I>> || <<I:1>> <= Inp >>, <<0>>, [global]) ].
[1,2,3]

【讨论】:

  • 哇,这确实有效。在撰写此评论时,我从未见过 ||、
  • 我对这种方法的一个抱怨是它返回尾随零作为列表的一部分。我希望它在 00 之后停止,并且结果中不包含 00。
  • 好的,所以我发现如果你添加修剪选项,你会得到想要的结果。
【解决方案2】:

你能把二进制转成字符串吗?

假设你可以,然后执行以下操作:

B = "1011011101111011111",
S = string:tokens(B, "0"),
R = lists:map(fun(E)->length(E) end, S).

但这不是有效的。期待好的答案。

【讨论】:

    【解决方案3】:

    这里是你可以如何解析标题:

    -module(bitcnt).
    -export([parse_header/1]).
    
    parse_header(Message) ->
            parse_header(Message, []).
    
    parse_header(<<0:1, 0:1, Body/bitstring>>, Header) ->
            %% stop if found header delimiter - two consecutive zero bits
            %% return parsed header and message body
            {lists:reverse(Header), Body};
    parse_header(<<1:1, Rest/bitstring>>, []) ->
            %% handle if first bit is '1'
            parse_header(Rest, [1]);
    parse_header(<<1:1, Rest/bitstring>>, [H | T]) ->
            %% handle consecutive '1' bits of header
            parse_header(Rest, [H+1 | T]);
    parse_header(<<0:1, Rest/bitstring>>, Header) ->
            %% handle delimiters inside header - '0' bit
            parse_header(Rest, [0 | Header]).
    

    让我们在 shell 中测试一下。假设这样的标题'10110111'(必须解析为[1,2,3])+分隔符'00'+一些正文>:

    2> B1 = <<1:1,0:1,1:1,1:1,0:1,1:1,1:1,1:1,0:1,0:1,12345:64>>.
    <<183,0,0,0,0,0,0,12,14,1:2>>
    3> 
    3> bitcnt:parse_header(B1).
    {[1,2,3],<<0,0,0,0,0,0,48,57>>}
    4> 
    4> <<12345:64>>.
    <<0,0,0,0,0,0,48,57>>
    

    另一个测试'11101'(必须解析成[3,1])+'00'+>

    5> B2 = <<1:1, 1:1, 1:1, 0:1, 1:1, 0:1, 0:1, 12345:64>>.
    <<232,0,0,0,0,0,0,96,57:7>>
    6> 
    6> bitcnt:parse_header(B2).                             
    {[3,1],<<0,0,0,0,0,0,48,57>>}
    

    即使标头为空(消息以两个连续的零位开头)- 函数将标头解析为空列表:

    7> B3 = <<0:1, 0:1, 12345:64>>.
    <<0,0,0,0,0,0,12,14,1:2>>
    8> 
    8> bitcnt:parse_header(B3).    
    {[],<<0,0,0,0,0,0,48,57>>}
    

    附言

    顺便说一句,您的标题格式非常多余。如果您想对大数字进行编码,例如数字 1024 - 您需要将其转换为 1024 个连续的“1”位!

    有两种方法可以改善标题格式:

      1234563预定义长度。例如 - 如果你所有的数字都小于 2^32 - 你需要 32 位来编码这个间隔中的每个数字
    • 如果您无法定义阈值(具有最大值的数字):使用可变长度编码。例如,Elias gamma-codingExponential-Golomb coding

    【讨论】:

    • 我阅读并重新阅读了您的答案,然后阅读了您提到的编码方法。除非我误解了,否则我的方法似乎与 Elias 伽马编码相去甚远。事实上,在这种情况下,我不会使用 Elias 伽马编码的唯一原因是我也必须对数据进行编码——需要一个几乎与数据一样长的零列表。为了编码一个 1024 的标头,我会给标头一个 10 个 1 的标头,后跟两个零,然后是二进制 1024 标头,然后是数据。
    • 顺便说一句,这意味着该数据包访问了一台名为 10000000000 的计算机(确实是一个奇怪的名称)。
    • @Feynman,那么,您将每个数字编码为相应的连续1 位字符串吗? 1-&gt;1, 2-&gt;11, 3-&gt;111, 4-&gt;1111 ?这是非常多余的。离Elias gamma coding很远。
    • @Feynman,例如,在 Elias 伽玛编码中,16 将表示为000010000(9 位)。但是如果用你的算法编码16,它将被表示为1111111111111111(16位)
    • @Feynman,但如果你不关心冗余 - 从我的回答中查看函数 parse_header,它实现了你的算法
    猜你喜欢
    • 2014-11-18
    • 2016-03-03
    • 1970-01-01
    • 1970-01-01
    • 2014-08-27
    • 1970-01-01
    • 2019-04-02
    • 1970-01-01
    • 2023-03-27
    相关资源
    最近更新 更多