Recent

Author Topic: Converting Hex string to binary string  (Read 28396 times)

440bx

  • Hero Member
  • *****
  • Posts: 6530
Re: Converting Hex string to binary string
« Reply #15 on: December 17, 2021, 11:41:07 am »
Just in case the OP might be interested, here is a HexToBin function from scratch (it should be fast :) ) and a few test cases for it:
Code: Pascal  [Select][+][-]
  1. {$APPTYPE CONSOLE}
  2.  
  3. program _HexToBin;
  4.  
  5.  
  6.  
  7. function HexToBin(InHex : pchar; OutBin : pchar; InBinSize : ptrint) : boolean;
  8. type
  9.   THEX_RANGE      = '0'..'F';
  10.  
  11. const
  12.   HexValid        : set of char = ['0'..'9', 'A'..'F'];
  13.  
  14.   BinTable        : array[THEX_RANGE] of pchar =
  15.   (
  16.    '0000', '0001', '0010', '0011',             {  0..3 }
  17.    '0100', '0101', '0110', '0111',             {  4..7 }
  18.    '1000', '1001',                             {  8..9 }
  19.  
  20.    { next 7 characters in the ASCII table are not hex digits                  }
  21.  
  22.    #0,     #0,     #0,     #0,     #0,     #0,     #0,
  23.  
  24.    '1010', '1011', '1100', '1101',             {  A..D }
  25.    '1110', '1111'                              {  E..F }
  26.   );
  27.  
  28.   LOWERCASE_CLEAR  = not (ord('a') - ord('A'));        { should be (not $20)  }
  29.  
  30.   NIBBLES_PER_BYTE = 2;
  31.   BITS_PER_NIBBLE  = bitsizeof(byte) div NIBBLES_PER_BYTE;
  32.  
  33. var
  34.   HexLen          : ptrint;
  35.  
  36.   c               : char;
  37.  
  38.   Hex             : pchar;                     { to walk the input            }
  39.   Bin             : pchar;                     { to set the output            }
  40.  
  41.  
  42. begin
  43.   result := FALSE;
  44.  
  45.   if InBinSize <= 0 then exit;                 { validate output buffer size  }
  46.  
  47.   OutBin^  := #0;                              { initialize to null           }
  48.  
  49.   HexLen := strlen(InHex);                     { it better be null terminated }
  50.  
  51.   if HexLen <= 0 then exit;
  52.  
  53.   if (HexLen * BITS_PER_NIBBLE) >= InBinSize then exit;    { insufficient     }
  54.  
  55.   Hex := InHex;
  56.   Bin := OutBin;
  57.  
  58.   while TRUE do
  59.   begin
  60.     c := Hex^;
  61.  
  62.     { uppercase the digit if necessary                                        }
  63.  
  64.     if c in ['a'..'f'] then byte(c) := byte(c) and byte(LOWERCASE_CLEAR);
  65.  
  66.     if not (c in HexValid) then break;
  67.  
  68.     { the four bytes that make up a nibble can be seen as a DWORD, move all   }
  69.     { four of them as a DWORD                                                 }
  70.  
  71.     PDWORD(Bin)^ := PDWORD(BinTable[c])^;
  72.     inc(Bin, BITS_PER_NIBBLE);          { or sizeof(DWORD), same thing        }
  73.  
  74.     inc(Hex);                           { point to next input hex digit       }
  75.  
  76.     if Hex^ = #0 then  { exit(TRUE); }  { everything went well                }
  77.     begin
  78.       Bin^   := #0;
  79.  
  80.       result := TRUE;
  81.       exit;
  82.     end;
  83.   end;
  84.  
  85.   { there must have been an invalid hex digit in the input, reset the         }
  86.   { output/result to empty                                                    }
  87.  
  88.   OutBin^ := #0;
  89. end;
  90.  
  91.  
  92.  
  93. const
  94.   TestData     : packed array[0..30] of pchar =
  95.   (
  96.    '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',  '8',  '9',  'A',  'B',
  97.    'C',  'D',  'E',  'F',
  98.  
  99.    '01', '10', '9a', '9A', 'a9', 'A9', 'f0', 'F0', 'FF', 'ff', 'aa', 'AA',
  100.  
  101.    'F01Abc48',       { result fits exactly in the 32 bytes used as buffer     }
  102.  
  103.    '123ABCZ',        { not a valid hex number                                 }
  104.  
  105.  
  106.    '012345678'       { bin doesn't fit in the buffer (size 32 + 1)            }
  107.   );
  108.  
  109.  
  110. var
  111.   OutBinBuffer : packed array[0..32] of char;        { 33 bytes               }
  112.  
  113.   i            : ptrint;
  114.  
  115.  
  116. begin
  117.   writeln;
  118.   writeln;
  119.  
  120.   for i := low(TestData) to high(TestData) do
  121.   begin
  122.     if HexToBin(TestData[i], OutBinBuffer, sizeof(OutBinBuffer)) then
  123.     begin
  124.       writeln('  hex : ', TestData[i]:8, '   bin : ', OutBinBuffer:32);
  125.       continue;
  126.     end;
  127.  
  128.     writeln;
  129.     writeln('  hex ', TestData[i],
  130.             ' is either not a valid hex number or' +
  131.             ' the buffer is too small for its binary representation');
  132.   end;
  133.  
  134.  
  135.   writeln;
  136.   writeln('  press ENTER/RETURN to end this program');
  137.   readln;
  138. end.                      

ETA:

added the missing null termination to the output buffer when the conversion is successful (line 78).
« Last Edit: December 17, 2021, 01:58:11 pm by 440bx »
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

BobDog

  • Sr. Member
  • ****
  • Posts: 394
Re: Converting Hex string to binary string
« Reply #16 on: December 17, 2021, 05:00:27 pm »

You could do it from scratch using case.
Code: Pascal  [Select][+][-]
  1. uses
  2. sysutils;
  3.  
  4. {$MODE objfpc}{$H+}
  5.  
  6.  
  7.  
  8. function hex2bin(hx:ansistring):ansistring;
  9. function TrimLeadingZeros(const S: ansistring): ansistring;
  10. var
  11.   I, L: Int32;
  12. begin
  13.   L:= Length(S);
  14.   I:= 1;
  15.   while (I < L) and (S[I] = '0') do Inc(I);
  16.   exit(Copy(S, I));
  17. end;
  18.  
  19. var
  20. b:ansistring='';
  21. n:int32;
  22. begin
  23.  
  24. hx:=uppercase(hx);
  25.       for n :=1 to length(hx) do
  26.       begin
  27.             case (char(hx[n])) of
  28.              '0':b:=b+'0000';
  29.              '1':b:=b+'0001';
  30.              '2':b:=b+'0010';
  31.              '3':b:=b+'0011';
  32.              '4':b:=b+'0100';
  33.              '5':b:=b+'0101';
  34.              '6':b:=b+'0110';
  35.              '7':b:=b+'0111';
  36.              '8':b:=b+'1000';
  37.              '9':b:=b+'1001';
  38.              'A':b:=b+'1010';
  39.              'B':b:=b+'1011';
  40.              'C':b:=b+'1100';
  41.              'D':b:=b+'1101';
  42.              'E':b:=b+'1110';
  43.              'F':b:=b+'1111';
  44.              else  
  45.              exit('not valid');
  46.             end;
  47.       end;
  48.       exit(TrimLeadingZeros(b));
  49. end;
  50.  
  51. begin
  52. writeln(hex2bin('123456789ABCDEF'));
  53. writeln('Press return to end . . .');
  54. readln;
  55. end.
  56.  
But I don't know about speed, although you can do big numbers.

 

440bx

  • Hero Member
  • *****
  • Posts: 6530
Re: Converting Hex string to binary string
« Reply #17 on: December 17, 2021, 05:41:16 pm »
But I don't know about speed, although you can do big numbers.
Your approach definitely has simplicity going for it.  It should be slower because of the string additions but, it shouldn't be noticeable unless the function is called a lot (probably over 100,000 times.)

Also, I'm not sure that the leading zeroes should be trimmed.  I think that if the hex input number included leading zeroes then the binary representation should show them as well.  If the programmer doesn't want leading zeroes then, the programmer should trim them in the hex string before sending it to HexToBin. 
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Josh

  • Hero Member
  • *****
  • Posts: 1458
Re: Converting Hex string to binary string
« Reply #18 on: December 17, 2021, 08:30:56 pm »
another routine

Code: Pascal  [Select][+][-]
  1. const
  2.  hexs=['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'];
  3.  
  4. var
  5.   bins:array[0..15] of string =('0000','0001','0010','0011','0100','0101','0110','0111','1000','1001','1010','1011','1100','1101','1110','1111');
  6.  
  7. function quickhex2bin(hx:string):string;
  8. var i,v:integer;
  9. begin
  10.   result:='';
  11.   if hx='' then exit;
  12.   hx:=upcase(hx);
  13.   for i:=1 to length(hx) do
  14.   begin
  15.     if hx[i] in hexs then
  16.     begin
  17.       val('$'+hx[i],v);
  18.       result:=result+bins[v];
  19.     end
  20.     else result:=result+'ERRR';
  21.   end;
  22. end;              
  23.  
« Last Edit: December 17, 2021, 08:34:11 pm by josh »
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Converting Hex string to binary string
« Reply #19 on: December 17, 2021, 09:01:41 pm »
Hi!

Toto - we're not in Kansas anymore.

We are in Pascal:

Code: Pascal  [Select][+][-]
  1. const
  2.  hexs=['0'..'9','A'..'F'];
  3.  

Winni

Josh

  • Hero Member
  • *****
  • Posts: 1458
Re: Converting Hex string to binary string
« Reply #20 on: December 18, 2021, 03:01:03 am »
Hi Wini,

updated for speed

on my dual core laptop
10 million 64bit hexes just under 10secs

Code: Pascal  [Select][+][-]
  1. const hexs=['0'..'9','A'..'F'];
  2.       hexsl=['0'..'9','a'..'f'];      // avoid using upcase 2 sets required
  3.       bins:array[0..15] of string =('0000','0001','0010','0011','0100','0101','0110','0111',
  4.                                  '1000','1001','1010','1011','1100','1101','1110','1111');
  5.  
  6.  
  7. function quickhex2bin(hx:String):String;
  8. var i:int16;
  9.     v:byte;
  10.  
  11. function quickhextobyte(const x:byte):byte; inline;    // for uppercase set
  12. begin
  13.   if x>64 then result:=x-55 else result:=x-48;
  14. end;
  15.  
  16. function quickhextobytel(const x:byte):byte; inline;  // for lowercase set
  17. begin
  18.   if x>96 then result:=x-87 else result:=x-48;
  19. end;
  20.  
  21. begin
  22.   result:='';
  23.   if hx='' then exit;
  24.   for i:=1 to length(hx) do
  25.   begin
  26.     if hx[i] in hexs then result:=result+bins[quickhextobyte(byte(hx[i]))]  // uppercase hex
  27.       else if hx[i] in hexsl then result:=result+bins[quickhextobytel(byte(hx[i]))]   // lowercase hex   to avoid using upcase
  28.         else result:=result+'ERRR';
  29.   end;
  30. end;    
  31.  

the code could be improved for speed by setting the length of result to 4*length of hx, then moving the bins element to correct location in result; this would negate the concatenation of result in each iteration of loop; but left as is for ease of reading
« Last Edit: December 18, 2021, 11:35:46 am by josh »
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

Bart

  • Hero Member
  • *****
  • Posts: 5727
    • Bart en Mariska's Webstek
Re: Converting Hex string to binary string
« Reply #21 on: December 18, 2021, 02:42:57 pm »
Code: Pascal  [Select][+][-]
  1. const hexs=['0'..'9','A'..'F'];
  2.       hexsl=['0'..'9','a'..'f'];      // avoid using upcase 2 sets required
  3. ...
  4.  

Hmm...
Code: Pascal  [Select][+][-]
  1. const hexs=['0'..'9','A'..'F', 'a'..'f''];  // no upcase required, no 2 sets required.
  2.      ...
  3.  

Bart

Josh

  • Hero Member
  • *****
  • Posts: 1458
Re: Converting Hex string to binary string
« Reply #22 on: December 18, 2021, 03:35:03 pm »
Hi Bart

moded routines.

Code: Pascal  [Select][+][-]
  1. const hexs=['0'..'9','A'..'F','a'..'f'];
  2.       bins:array[0..15] of string =('0000','0001','0010','0011','0100','0101','0110','0111','1000','1001','1010','1011','1100','1101','1110','1111');
  3.  
  4. function QuickHex2Bin(hx:String):String;
  5. var i:int16;
  6.  
  7.   function QuickHexToValue(const x:byte):byte; inline;
  8.   begin
  9.     if x<58 then result:=x-48 else if x<71 then result:=x-55 else if x<103 then result:=x-87;
  10.   end;
  11.  
  12. begin
  13.   result:='';
  14.   if hx='' then exit;
  15.   for i:=1 to length(hx) do if hx[i] in hexs then result:=result+bins[QuickHexToValue(byte(hx[i]))] else result:=result+'ERRR';
  16. end;      
  17.  
« Last Edit: December 18, 2021, 03:40:59 pm by josh »
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

440bx

  • Hero Member
  • *****
  • Posts: 6530
Re: Converting Hex string to binary string
« Reply #23 on: December 18, 2021, 03:46:44 pm »
Hi Josh,

My concern with your algorithm is that it needs a lot of "magic" numbers to correctly map a character's ordinal to a correct index in the bins table.

From a performance viewpoint, some characters will require 3 comparisons to calculate the correct index and, as you already know, the string concatenation is expensive but, at least it is logically much clearer than the DWORD "trick" I use.

FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

440bx

  • Hero Member
  • *****
  • Posts: 6530
Re: Converting Hex string to binary string
« Reply #24 on: December 18, 2021, 03:55:56 pm »
what's this, battle of wits ?  :o
No, it's a battle of bits! :)

Welcome to the bit race ;)
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Josh

  • Hero Member
  • *****
  • Posts: 1458
Re: Converting Hex string to binary string
« Reply #25 on: December 18, 2021, 04:17:08 pm »
Hi 440bx,

Aim was to use as few string routines to increase performance; and make hopefully readable; i could add the setlength and move to make it a lot faster; but its  not so readable..

comparisons done in probable order ie 0..9 first; then A-F then a-f;

could also add constants to make more readible ie
Code: [Select]
const Ascii_Upper_F=71;Ascii_Lower_f=103;Ascii_9=58;  // all + 1 to allow just a < comparison
      Deduct_Ascii_Upper_F=55;Deduct_Ascii_Lower_f=87;Deduct_Ascii_9=48;

function QuickHexToValue(const x:byte):byte; inline;
  begin
    if x<ascii_9 then result:=x-deduct_Ascii_9 else if x<Ascii_Upper_F then result:=x-Deduct_Ascii_Upper_F else if x<Ascii_Lower_f then result:=x-Deduct_Ascii_Lower_f;
  end; 

« Last Edit: December 18, 2021, 04:42:06 pm by josh »
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

bytebites

  • Hero Member
  • *****
  • Posts: 787
Re: Converting Hex string to binary string
« Reply #26 on: December 18, 2021, 10:34:05 pm »
upcase
Code: Pascal  [Select][+][-]
  1. ord(ch) and 223

mas steindorff

  • Hero Member
  • *****
  • Posts: 589
Re: Converting Hex string to binary string
« Reply #27 on: December 19, 2021, 01:18:40 am »
lots of solutions here but it even simpler.
I just use the standard string to number functions and then just add a '$' char to the start of the hex string and it has always worked for me.  I put this one liner into a function you can Get the string from wherever ...

Code: Pascal  [Select][+][-]
  1. function funwithHex(str:string):int64;
  2. begin
  3.   if  TryStrToInt64('$'+str, result) then
  4.       result := 0;
  5. end;
  6.  
   

I've also just used
   i:= strToInt('$'+str);
and other simple conversions successfully, your choice.
you can add simple checking if your string source is not clean.  something like
  str := StringReplace('$$','$','$'+str);
before the conversion if speed is not an issue.
MAS
 
windows 10 &11, Ubuntu 21+ IDE 3.4 general releases

440bx

  • Hero Member
  • *****
  • Posts: 6530
Re: Converting Hex string to binary string
« Reply #28 on: December 19, 2021, 01:33:02 am »
lots of solutions here but it even simpler.
if the goal is exclusively limited to simplicity then nothing beats @Remy Lebeau's suggestion in post https://forum.lazarus.freepascal.org/index.php/topic,57555.msg428125.html#msg428125

FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Kays

  • Hero Member
  • *****
  • Posts: 632
  • Whasup!?
    • KaiBurghardt.de
Re: Converting Hex string to binary string
« Reply #29 on: December 19, 2021, 01:50:23 am »
if the goal is exclusively limited to simplicity then nothing beats @Remy Lebeau's suggestion in post https://forum.lazarus.freepascal.org/index.php/topic,57555.msg428125.html#msg428125
Well, it uses the strUtils unit. Besides val as suggested by winni, the FPC’s implementation of read/readLn/readStr can be used if it’s known the digits are legit, e.g.:
Code: Pascal  [Select][+][-]
  1. {$longStrings on}
  2. var
  3.         s: string;
  4.         i: ALUSint;
  5. begin
  6.         readLn(s);
  7.         readStr('$' + s, i); { or `0x` prefix }
  8.         writeLn(i:20);
  9. end.
This will save you another dependency.
Yours Sincerely
Kai Burghardt

 

TinyPortal © 2005-2018