peiman F.
peiman F.

Reputation: 1658

delphi a fast way to convert hex string to binary string

I have to convert a hex string to a binary string like this

"F" -> "‭1111‬"
"A" -> "‭1010‬"
"1AF1" -> "1101011110001"

I have this function:

function HexToBin(Hexadecimal: string): string;
const
  BCD: array [0..15] of string =
    ('0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111',
    '1000', '1001', '1010', '1011', '1100', '1101', '1110', '1111');
var
  i: integer;
begin
  Result := '';
  for i := Length(Hexadecimal) downto 1 do
    Result := BCD[StrToInt('$' + Hexadecimal[i])] + Result;
end; 

and I call it like this:

for I := 0 to Trunc(tcpFrame.Length/100) do
begin
   str := copy(tcpFrame , (I*100)+1 , 100  );
   data  := data +  HexToBin( str );
end;

but this is really slow on large data like 50-60 MB of string

Is there a better and faster way for this?

Upvotes: 0

Views: 1702

Answers (1)

Andreas Rejbrand
Andreas Rejbrand

Reputation: 109158

I believe the main problem is that you append to the string one part at a time.

In Delphi, building a dynamic array or string by adding a single small part at a time is very slow. A better approach is to pre-allocate the array/string to a large size and then fill its contents. If you know the final length at the beginning, use this size; otherwise, allocate in large chunks.

In this case, we do know the final length.

A sample implementation of this idea:

function HexStrToBinStr(const AHexStr: string): string;
var
  i, j: Integer;
const
  HexParts: array[0..$F] of string =
    (
      {0} '0000',
      {1} '0001',
      {2} '0010',
      {3} '0011',
      {4} '0100',
      {5} '0101',
      {6} '0110',
      {7} '0111',
      {8} '1000',
      {9} '1001',
      {A} '1010',
      {B} '1011',
      {C} '1100',
      {D} '1101',
      {E} '1110',
      {F} '1111'
    );
begin
  SetLength(Result, 4 * AHexStr.Length);
  j := 1;
  for i := 1 to AHexStr.Length do
  begin
    case AHexStr[i] of
      '0'..'9':
        Move(HexParts[Ord(AHexStr[i]) - Ord('0')][1], Result[j], sizeof(char) * 4);
      'A'..'F':
        Move(HexParts[$A + Ord(AHexStr[i]) - Ord('A')][1], Result[j], sizeof(char) * 4);
      'a'..'f':
        Move(HexParts[$A + Ord(AHexStr[i]) - Ord('a')][1], Result[j], sizeof(char) * 4);
    else
      raise EConvertError.CreateFmt('Invalid hexadecimal string "%s".', [AHexStr]);
    end;
    Inc(j, 4);
  end;
end;

The fact that I don't compute your intermediate '$' + Hexadecimal[i] string and call StrToInt might also speed things up a bit.

Bonus exercise: As a bonus exercise, modify this code by adding a parameter APrettyPrint: Boolean = False that, if True, adds a space between each group of four bits. Of course you should still preallocate.

Upvotes: 7

Related Questions