how to convert byte array to its hex representation in Delphi
Asked Answered
I

5

2

I have TBytes variable with a value [0,0,15,15]. How can I convert it to "00FF" ?

I dont want to use loops, bcoz this logic to be used in time intensive function.

(I tried using BinToHex, but I could not get it working with string variable.)

Thanks & Regards,

Pavan.

Issuance answered 29/6, 2009 at 20:25 Comment(0)
M
5
// Swapping is necessary because x86 is little-endian.
function Swap32(value: Integer): Integer;
asm
  bswap eax
end;

function FourBytesToHex(const bytes: TBytes): string;
var
  IntBytes: PInteger;
  FullResult: string;
begin
  Assert(Length(bytes) = SizeOf(IntBytes^));
  IntBytes := PInteger(bytes);
  FullResult := IntToHex(Swap32(IntBytes^), 8);
  Result := FullResult[2] + FullResult[4] + FullResult[6] + FullResult[8];
end;

If that last line looks a little strange, it's because you requested a four-byte array be turned into a four-character string, whereas in the general case, eight hexadecimal digits are required to represent a four-byte value. I'm simply assumed that your byte values are all below 16, so only one hexadecimal digit is needed. If your example was a typo, then simply replace the last two lines with this one:

Result := IntToHex(Swap32(IntBytes^), 8);

By the way, your requirement forbidding loops will not be met. IntToHex uses a loop internally.

Monnet answered 29/6, 2009 at 20:47 Comment(5)
IntToHex(IntBytes^, 8); generates the string in reverse order. If you see my question i need it as "00FF", but the above function generates it as "FF00" .. Can i specify byte order ?Issuance
Result := FullResult[8] + FullResult[6] + FullResult[4] + FullResult[2];Teddi
is there any way to specify byte order instead of accessing it element by element..??Issuance
Sorry. I forgot that x86 is little-endian, so you need to swap the byte order.Monnet
@Pavan: couldn't you simply add a boolean byte order parameter to the function header, then use the last line from Rob's original answer and Nosredna's comment as alternatives in an if statement?Ballata
H
3
function ByteToHex(InByte:byte):shortstring;
const Digits:array[0..15] of char='0123456789ABCDEF';
begin
 result:=digits[InByte shr 4]+digits[InByte and $0F];
end;

Example :
MyHex := ByteTohex($FF);
the result
MyHex is "FF".

MyHex := ByteTohex(255);
the result
MyHex is "FF".

MyHex := ByteTohex($55);
the result
MyHex is "55".
Headmistress answered 29/6, 2009 at 20:27 Comment(3)
it is TBytes and not a single byte.. and i dont want to use loops to compute each and every value. If I am not wrong, your logic works per byte..Issuance
This looks like the best solution you're going to get. You're gonna have to use a loop somewhere. If you want to use it on a TBytes, wrap this in a function that takes a TBytes and loops through it, building the output string one byte at a time.Josephina
This could be adapted to a "no loop" solution. If you really care about that requirement.Teddi
G
3

This one is quite fast and works with any array size.. It's like BinToHex, but instead of expecting 0..255 byte values, it only uses the low nibble.

procedure BinToSingleHex(Buffer, Text: PAnsiChar; BufSize: Integer);
const
  Convert: array[0..15] of AnsiChar = '0123456789ABCDEF';
var
  I: Integer;
begin
  for I := 0 to BufSize - 1 do
  begin
    Text[0] := Convert[Byte(Buffer[I]) and $F];
    Inc(Text);
  end;
end;

Assembler that does the same:

procedure BinToSingleHex(Buffer, Text: PAnsiChar; BufSize: Integer);assembler;
asm
        PUSH    ESI
        PUSH    EDI
        MOV     ESI,EAX
        MOV     EDI,EDX
        MOV     EDX,0
        JMP     @@1
@@0:    DB      '0123456789ABCDEF'
@@1:    LODSB
        AND     DL,AL
        AND     DL,0FH
        MOV     AL,@@0.Byte[EDX]
        STOSB
        DEC     ECX
        JNE     @@1
        POP     EDI
        POP     ESI
end;

usage:

type  THexDigit=0..15;
const ArSize=16;
var   Ar:array[0..Pred(ArSize)] of THexDigit=(0,1,2,3,4,5,6,7,8,9,8,7,6,5,4,3);
      S:Array[0..Pred(ArSize)] of AnsiChar;

BinToSingleHex(@Ar,S,Length(Ar));
WriteLn(S);
Gagliano answered 29/6, 2009 at 21:21 Comment(2)
Beware. Your result string is not null-terminated. Also, your code requires the "typed @ operator" compiler directive to be disabled because you pass a byte pointer to a parameter expecting an AnsiChar pointer. Better to change the first parameter's type to PByte. And how do you figure that you need to use "and" to clear the upper four bits in Delphi but you don't need to in assembler?Monnet
Good points.. and yeah, you're right.. of course it's still needed to mask most significant 4 bits there. fixed thatGagliano
H
1

Bit late to the party but why not a simple lookup table?

const
HexChars : Array[0..15] of Char = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');

Assuming TBytes values of 0..15

Function (ABytea: TBytes): string
    begin 
      Result  := HexChars[ABytea[0]];
      Result  := Result + HexChars[ABytea[1]];
      Result  := Result + HexChars[ABytea[2]];
      Result  := Result + HexChars[ABytea[3]];
    end;

of course neater with a loop :) and needs modifying for byte values above 15:

begin 
  Result  := HexChars[ABytea[0] shr 4];
  Result  := Result + HexChars[ABytea[0] and $0F];
  Result  := Result + HexChars[ABytea[1] shr 4];
  Result  := Result + HexChars[ABytea[1] and $0F];
  Result  := Result + HexChars[ABytea[2] shr 4];
  Result  := Result + HexChars[ABytea[2] and $0F];
  Result  := Result + HexChars[ABytea[3] shr 4];
  Result  := Result + HexChars[ABytea[3] and $0F];
end;

Still neater with a loop especially if TBytes gets larger

Homogeny answered 2/7, 2009 at 23:35 Comment(0)
I
0

I had the same problem. My solution using System.SysUtils.TByteHelper.ToHexString (with loop)

function ToHexString(const MinDigits: Integer): string; overload; inline;

Example code:

procedure TForm1.Button2Click(Sender: TObject);
begin
var text:string;
var w:integer:=0;
var bytearray: Tarray<byte>:= [$DE, $AD, $BE, $EF];
  repeat
            text:= text+ pbyte(@bytearray[w])^.ToHexString(2);
            inc(w);
    until w >= high(bytearray);
end;

bytearray := $DE $AD $BE $EF

text := DEADBEEF

Infundibuliform answered 22/5, 2022 at 16:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.