'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 |
