'Erlang io:formatting a binary to hex

Can I format an Erlang binary so that each byte is written in hex? I.e.,

> io:format(???, [<<255, 16>>]).
<<FF, 10>>

I don't see an obvious way to do it in io:format documentation, but perhaps I am simply missing one? Converting a binary to list and formatting its elements separately is too inefficient.



Solution 1:[1]

Improving upon @hairyhum

This takes care of zero paddings << <<Y>> ||<<X:4>> <= Id, Y <- integer_to_list(X,16)>>

reverse transformation <<<<Z>> || <<X:8,Y:8>> <= Id,Z <- [binary_to_integer(<<X,Y>>,16)]>>, %%hex to binary

Solution 2:[2]

This hasn’t seen any action for a while, but all of the prior solutions seem overly convoluted. Here’s what, for me, seems much simpler:

[begin if N < 10 -> 48 + N; true -> 87 + N end end || <<N:4>> <= Bin]

If you prefer it expanded a bit:

[begin
    if
        N < 10 ->
            48 + N; % 48 = $0
        true ->
            87 + N  % 87 = ($a - 10)
    end
end || <<N:4>> <= Bin]

Solution 3:[3]

You could do: [ hd(erlang:integer_to_list(Nibble, 16)) || << Nibble:4 >> <= Binary ].

Which would return you a list(string) containing the hex digits of the binary. While I doubt the efficiency of this operation is going to have any effect on the runtime of your system, you could also have this bin_to_hex function return an iolist which is simpler to construct and will be flattened when output anyway. The following function returns an iolist with the formatting example you gave:

bin_to_hex(Bin) when is_binary(Bin) ->
    JoinableLength = byte_size(Bin) - 1,
    << Bytes:JoinableLength/binary, LastNibble1:4, LastNibble2:4 >> = Bin,
    [ "<< ",
      [ [ erlang:integer_to_list(Nibble1, 16), erlang:integer_to_list(Nibble2, 16), ", " ]
        || << Nibble1:4, Nibble2:4 >> <= Bytes ],
      erlang:integer_to_list(LastNibble1, 16),
      erlang:integer_to_list(LastNibble2, 16),
      " >>" ].

It's a bit ugly, but runs through the binary once and doesn't traverse the output list (otherwise I'd have used string:join to get the interspersed ", " sequences). If this function is not the inner loop of some process (I have a hard time believing this function will be your bottleneck), then you should probably go with some trivially less efficient, but far more obvious code like:

bin_to_hex(Bin) when is_binary(Bin) ->
    "<< " ++ string:join([byte_to_hex(B) || << B >> <= Bin ],", ") ++ " >>".

byte_to_hex(<< N1:4, N2:4 >>) ->
    [erlang:integer_to_list(N1, 16), erlang:integer_to_list(N2, 16)].

Solution 4:[4]

Here is another short and fast version which I use:

hexlify(Bin) when is_binary(Bin) ->
    << <<(hex(H)),(hex(L))>> || <<H:4,L:4>> <= Bin >>.

hex(C) when C < 10 -> $0 + C;
hex(C) -> $a + C - 10.

Solution 5:[5]

if you prefer to make a binary string instead of erlang default list strings, you may use binary comprehension syntax, like what I did on my sha1 generating code:

1> << << if N >= 10 -> N -10 + $a;
1>          true    -> N     + $0 end >>
1>    || <<N:4>> <= crypto:hash(sha, "hello world") >>.
<<"2aae6c35c94fcfb415dbe95f408b9ce91ee846ed">>

same as in python binascii.b2a_hex:

>>> binascii.b2a_hex(sha.new('hello world').digest())
'2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'

Solution 6:[6]

bin_to_hex_list(Bin) when is_binary(Bin) ->
  lists:flatten([integer_to_list(X,16) || <<X>> <= Bin]).

Solution 7:[7]

As of OTP24, there is

1> binary:encode_hex(<<1,2,3,4,5,6,255>>).
<<"010203040506FF">>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 TedB
Solution 3 archaelus
Solution 4 jmuc
Solution 5
Solution 6 hairyhum
Solution 7 dischoen