4

​for example:

I have a binary look like this:

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

or

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

and so on...

The format is

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

I want parse binary get

  ["ok", "bcd"]

how to implement in Elixir or Erlang ?

Go version

a Go version parse this

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{}
}

Thanks

2 Answers 2

8

A more flexible solution:

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
Sign up to request clarification or add additional context in comments.

4 Comments

thanks, I add a version to description this question
Note that this solution is not good for parsing a large amount of data because it will iterate over several times. The 2nd solution in lidashuang's answer will avoid that issue.
I've updated mine to use the Stream functions instead of Enum. This will sidestep the multiple iteration issue as well.
note that in newer elixir versions you will have to write binary-size(byte_size_int) instead of [binary, size(byte_size_int)]
4

I wrote a solution:

  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

The Elixir mailing list provides another one:

  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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.