【问题标题】:Elixir parse binary data?Elixir 解析二进制数据?
【发布时间】:2016-06-29 06:24:31
【问题描述】:

​例如:

我有一个像这样的二进制文件:

 bin1 = "2\nok\n3\nbcd\n\n"​

 bin2 = "2\nok\n3\nbcd\n1\na\n\n"​

等等……

格式是

 byte_size  \n  bytes \n byte_size  \n  bytes \n  \n 

我要解析二进制获取

  ["ok", "bcd"]

如何在 Elixir 或 Erlang 中实现?

Go 版本

一个 Go 版本解析这个

func (c *Client) parse() []string {
    resp := []string{}
    buf := c.recv_buf.Bytes()
    var idx, offset int
    idx = 0
    offset = 0

    for {
        idx = bytes.IndexByte(buf[offset:], '\n')
        if idx == -1 {
            break
        }
        p := buf[offset : offset+idx]
        offset += idx + 1
        //fmt.Printf("> [%s]\n", p);
        if len(p) == 0 || (len(p) == 1 && p[0] == '\r') {
            if len(resp) == 0 {
                continue
            } else {
                c.recv_buf.Next(offset)
                return resp
            }
        }

        size, err := strconv.Atoi(string(p))
        if err != nil || size < 0 {
            return nil
        }
        if offset+size >= c.recv_buf.Len() {
            break
        }

        v := buf[offset : offset+size]
        resp = append(resp, string(v))
        offset += size + 1
    }

    return []string{}
}

谢谢

【问题讨论】:

    标签: erlang elixir


    【解决方案1】:

    更灵活的解决方案:

    result = bin 
    |> String.split("\n") 
    |> Stream.chunk(2)
    |> Stream.map(&parse_bytes/1)
    |> Enum.filter(fn s -> s != "" end)
    
    def parse_bytes(["", ""]), do: ""
    def parse_bytes([byte_size, bytes]) do
      byte_size_int = byte_size |> String.to_integer
      <<parsed :: binary-size(byte_size_int)>> = bytes
      parsed
    end
    

    【讨论】:

    • 谢谢,我添加一个版本来描述这个问题
    • 请注意,这种解决方案不适合解析大量数据,因为它会迭代多次。丽达双回答中的第二个解决方案将避免该问题。
    • 我已经更新了我的使用 Stream 函数而不是 Enum。这也将回避多次迭代问题。
    • 请注意,在较新的 elixir 版本中,您必须编写 binary-size(byte_size_int) 而不是 [binary, size(byte_size_int)]
    【解决方案2】:

    我写了一个解决方案:

      defp parse("\n") do
        []
      end
    
      defp parse(data) do
        {offset, _} = :binary.match(data, "\n")
        size = String.to_integer(binary_part(data, 0, offset))
        value = binary_part(data, offset + 1, size)
    
        len = offset + 1 + size + 1
        [value] ++ parse(binary_part(data, len, byte_size(data) - len))
      end
    

    Elixir 邮件列表提供了另一个:

      defp parse_binary("\n"), do: []
    
      defp parse_binary(binary) do
        {size, "\n" <> rest} = Integer.parse(binary)
        <<chunk :: [binary, size(size)], "\n", rest :: binary>> = rest
        [chunk|parse_binary(rest)]
      end
    

    【讨论】:

      猜你喜欢
      • 2011-11-23
      • 2014-11-12
      • 1970-01-01
      • 2011-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-31
      • 1970-01-01
      相关资源
      最近更新 更多