2010-09-22 17 views
17

¿Puedo formatear un binario de Erlang para que cada byte se escriba en hexadecimal? Es decir,Erlang io: formateo de un binario a hexadecimal

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

no veo una manera obvia de hacerlo en io:format documentación, pero tal vez simplemente estoy perdiendo uno? Convertir un binario en una lista y formatear sus elementos por separado es demasiado ineficiente.

Respuesta

21

No, no hay tal opción de formateo, pero se puede hacer algo como:

io:format("<<~s>>~n", [[io_lib:format("~2.16.0B",[X]) || <<X:8>> <= <<255,16>> ]]). 

Hay solución mucho más rápido si es necesario.

-module(bin_to_hex). 

-compile([native, {hipe, [o3]}]). 

-export([bin_to_hex/1]). 

bin_to_hex(B) when is_binary(B) -> 
    bin_to_hex(B, <<>>). 

-define(H(X), (hex(X)):16). 

bin_to_hex(<<>>, Acc) -> Acc; 
bin_to_hex(Bin, Acc) when byte_size(Bin) band 7 =:= 0 -> 
    bin_to_hex_(Bin, Acc); 
bin_to_hex(<<X:8, Rest/binary>>, Acc) -> 
    bin_to_hex(Rest, <<Acc/binary, ?H(X)>>). 

bin_to_hex_(<<>>, Acc) -> Acc; 
bin_to_hex_(<<A:8, B:8, C:8, D:8, E:8, F:8, G:8, H:8, Rest/binary>>, Acc) -> 
    bin_to_hex_(
    Rest, 
    <<Acc/binary, 
     ?H(A), ?H(B), ?H(C), ?H(D), ?H(E), ?H(F), ?H(G), ?H(H)>>). 

-compile({inline, [hex/1]}). 

hex(X) -> 
    element(
    X+1, {16#3030, 16#3031, 16#3032, 16#3033, 16#3034, 16#3035, 16#3036, 
      16#3037, 16#3038, 16#3039, 16#3041, 16#3042, 16#3043, 16#3044, 
      16#3045, 16#3046, 16#3130, 16#3131, 16#3132, 16#3133, 16#3134, 
      16#3135, 16#3136, 16#3137, 16#3138, 16#3139, 16#3141, 16#3142, 
      16#3143, 16#3144, 16#3145, 16#3146, 16#3230, 16#3231, 16#3232, 
      16#3233, 16#3234, 16#3235, 16#3236, 16#3237, 16#3238, 16#3239, 
      16#3241, 16#3242, 16#3243, 16#3244, 16#3245, 16#3246, 16#3330, 
      16#3331, 16#3332, 16#3333, 16#3334, 16#3335, 16#3336, 16#3337, 
      16#3338, 16#3339, 16#3341, 16#3342, 16#3343, 16#3344, 16#3345, 
      16#3346, 16#3430, 16#3431, 16#3432, 16#3433, 16#3434, 16#3435, 
      16#3436, 16#3437, 16#3438, 16#3439, 16#3441, 16#3442, 16#3443, 
      16#3444, 16#3445, 16#3446, 16#3530, 16#3531, 16#3532, 16#3533, 
      16#3534, 16#3535, 16#3536, 16#3537, 16#3538, 16#3539, 16#3541, 
      16#3542, 16#3543, 16#3544, 16#3545, 16#3546, 16#3630, 16#3631, 
      16#3632, 16#3633, 16#3634, 16#3635, 16#3636, 16#3637, 16#3638, 
      16#3639, 16#3641, 16#3642, 16#3643, 16#3644, 16#3645, 16#3646, 
      16#3730, 16#3731, 16#3732, 16#3733, 16#3734, 16#3735, 16#3736, 
      16#3737, 16#3738, 16#3739, 16#3741, 16#3742, 16#3743, 16#3744, 
      16#3745, 16#3746, 16#3830, 16#3831, 16#3832, 16#3833, 16#3834, 
      16#3835, 16#3836, 16#3837, 16#3838, 16#3839, 16#3841, 16#3842, 
      16#3843, 16#3844, 16#3845, 16#3846, 16#3930, 16#3931, 16#3932, 
      16#3933, 16#3934, 16#3935, 16#3936, 16#3937, 16#3938, 16#3939, 
      16#3941, 16#3942, 16#3943, 16#3944, 16#3945, 16#3946, 16#4130, 
      16#4131, 16#4132, 16#4133, 16#4134, 16#4135, 16#4136, 16#4137, 
      16#4138, 16#4139, 16#4141, 16#4142, 16#4143, 16#4144, 16#4145, 
      16#4146, 16#4230, 16#4231, 16#4232, 16#4233, 16#4234, 16#4235, 
      16#4236, 16#4237, 16#4238, 16#4239, 16#4241, 16#4242, 16#4243, 
      16#4244, 16#4245, 16#4246, 16#4330, 16#4331, 16#4332, 16#4333, 
      16#4334, 16#4335, 16#4336, 16#4337, 16#4338, 16#4339, 16#4341, 
      16#4342, 16#4343, 16#4344, 16#4345, 16#4346, 16#4430, 16#4431, 
      16#4432, 16#4433, 16#4434, 16#4435, 16#4436, 16#4437, 16#4438, 
      16#4439, 16#4441, 16#4442, 16#4443, 16#4444, 16#4445, 16#4446, 
      16#4530, 16#4531, 16#4532, 16#4533, 16#4534, 16#4535, 16#4536, 
      16#4537, 16#4538, 16#4539, 16#4541, 16#4542, 16#4543, 16#4544, 
      16#4545, 16#4546, 16#4630, 16#4631, 16#4632, 16#4633, 16#4634, 
      16#4635, 16#4636, 16#4637, 16#4638, 16#4639, 16#4641, 16#4642, 
      16#4643, 16#4644, 16#4645, 16#4646}). 

que realiza 90MB/s en la mía i5 portátil CPU M 520 @ 2.40GHz cuando se prueba en trozos 10MB. Pero la optimización fue llevada al extremo allí. También puede hacer 97MB si utiliza la búsqueda de 16 bits, pero es loco y demasiado largo para publicar aquí.

4

Puede hacer: [hd (erlang: integer_to_list (Nibble, 16)) || < < Nibble: 4 >> < = Binary].

que le devolverá una lista (cadena) que contiene los dígitos hexadecimales del binario. Aunque dudo que la eficacia de esta operación tenga algún efecto en el tiempo de ejecución de su sistema, también podría hacer que esta función bin_to_hex devuelva un iolist que es más simple de construir y se aplanará cuando se emita de todos modos. La siguiente función devuelve un iolist con el ejemplo de formato le dio:

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), 
     " >>" ]. 

Es un poco feo, pero funciona a través del binario de una vez y no atraviesa la lista de salida (cadena de otro modo, hubiera utilizado: unirse a obtener las secuencias "," intercaladas). Si esta función no es el bucle interno de algún proceso (no tengo un tiempo difícil creer esta función será su cuello de botella), entonces probablemente debería ir con un poco trivial menos eficiente, pero el código mucho más obvia, como:

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)]. 
0
bin_to_hex_list(Bin) when is_binary(Bin) -> 
    lists:flatten([integer_to_list(X,16) || <<X>> <= Bin]). 
+2

esto no hace ningún relleno cero, por lo que los resultados son incorrectos. Si los primeros 4 bits de un byte son 0, entonces solo producirá un dígito para ese byte cuando se necesiten dos. –

6

Mejorando @hairyhum

Este se encarga de cero rellenos << <<Y>> ||<<X:4>> <= Id, Y <- integer_to_list(X,16)>>

transformación inversa <<<<Z>> || <<X:8,Y:8>> <= Id,Z <- [binary_to_integer(<<X,Y>>,16)]>>, %%hex to binary

2

Esto no ha visto ninguna acción por un tiempo, pero todas las soluciones anteriores parecen excesivamente intrincadas. Esto es lo que, para mí, parece mucho más simple:

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

Si prefiere que se expandió un poco:

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

Aquí hay otra versión corta y rápida que utilizo:

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. 
1

si prefiere hacer una cadena binaria en lugar de las cadenas de lista predeterminadas de erlang, puede usar la sintaxis de comprensión binaria, como lo que hice en mi código de generación sha1:

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

mismo que en binascii.b2a_hex pitón:

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