# convert a hex string to binary equivalent

#### Do you have a question? Post it now! No Registration Necessary.  Now with pictures!

•  Subject
• Author
• Posted on
For some code supposed to help with handling 'users' imported from AD, I
need to convert the AD GUID from a representation as 'string of hex
digits' to the corresponding binary string. An example I've been using
is

DEB328A2C4AC4DA6E46B13E38BC83F

Input can be assumed as valid because the text representations
themselves were originally created from binary data. Presently, I'm
using

---------
my \$text = 'DEB328A2C4AC4DA6E46B13E38BC83F';
my \$bin;

\$bin = pack('(H2)*', \$text =~ /../g);

print(join(',', map { sprintf('%02X', \$_) } unpack('C*', \$bin)), "\n");
---------

Other suggestions?

## Re: convert a hex string to binary equivalent

El 29/10/15 a las 23:17, Rainer Weikusat escribió:

Maybe.

while (\$text =~ /../g){
\$bin .= chr(hex());
}

--
http://gamo.eu.pn/
The generation of random numbers is too important to be left to chance

## Re: convert a hex string to binary equivalent

On 30/10/2015 00:17, Rainer Weikusat wrote:

why a    pack('H*', \$text)  is not enough ;

## Re: convert a hex string to binary equivalent

It is enough. I just didn't think of it yesterday evening.

## Re: convert a hex string to binary equivalent

says...

You're probably not going to care for this but this is how my mind works and how I like my
code to look -- maybe a few extra lines but very clear, easy to read and maintain.

sub hex2bin
{
my \$hex = shift;
my \$bin = '';
my %hex_conv = (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');

foreach my \$digit (split('', \$hex)) {
\$bin .= \$hex_conv;
}

return \$bin;
}

## Re: convert a hex string to binary equivalent

[...]

What I needed is a string of bytes whose numerical value corresponds
with a two-digit hex number from the input, eg 'ABCD' should end up as
"\xab\xcd".

I wouldn't want to write something like the hash definition above
because writing fifteen lines with meticolously placed zeroes is a huge
invitation for something to go wrong and everybody who reads this has to
'parse' all of them to ensure that the mappings are correct. The
computer can generate this:

my %nibbles = map { sprintf('%x', \$_), sprintf('%b', \$_) } 0 .. 15;

And I wouldn't want to write

foreach my \$digit (split('', \$hex)) {
\$bin .= \$nibbles;
}

\$bin .= \$nibbles for split(//, \$hex);

That's arguably a matter of style but the programming language is there
to help me: I don't have to tell it "use this variable" for as long as
the default will work fine and I don't have to create "(2D) areas fenced
by curlies" just to execute a single statement.

Since this is less text, it's  easier to read (less physical work
necessary for doing so required) and there's again less one can get
wrong while typing it.

I'd probably use

\$bin = \$hex;
\$bin =~ s/(.)/sprintf('%b', hex(\$1))/eg;

in real code despite this is a lot slower because there's yet less
mechanical detail about the procedure in it.

## Re: convert a hex string to binary equivalent

faster

print unpack("B*", pack('H*', \$text));

## Re: convert a hex string to binary equivalent

That's a discussion we already had some time ago and I suggested the
pack/ unpack solution back then: It's faster but not identical as it
will append 0000 if there's an odd number of hex digits. This would need
to be something like

sub packed
{
my \$x;

\$x = unpack('B*', pack('H*', \$_[0]));
\$x = substr(\$x, 0, -4) if length(\$_[0]) & 1;
}

That's still a lot faster than either s/// or an explicit loop but again
more 'mechanical' than the substitution.

------------
use Benchmark;

my %nibbles = map { sprintf('%x', \$_), sprintf('%b', \$_) } 0 .. 15;

my \$v = 'fdeaffb';

sub substed
{
my \$x = \$_[0];

\$x =~ s/(.)/sprintf('%b', hex(\$1))/eg;
\$x;
}

sub packed
{
my \$x;

\$x = unpack('B*', pack('H*', \$_[0]));
\$x = substr(\$x, 0, -4) if length(\$_[0]) & 1;
}

sub ford
{
my \$x;

\$x .= \$nibbles for split(//, \$_[0]);
\$x;
}

print(substed(\$v), "\n", packed(\$v), "\n", ford(\$v), "\n");

timethese(-3,
{
ford => sub { ford(\$v) },
substed => sub { substed(\$v) },
packed => sub { packed(\$v) }});

## Re: convert a hex string to binary equivalent

length(\$_[0]) % 2

: )

## Re: convert a hex string to binary equivalent

says...

Ok, you said

which I read as convert a string of hex digits to a string of binary digits.

I didn't think you'd care for my coding style.  I never or almost never use the default
variable, preferring instead to explictly name my variables.  Its more clear to me what is
going on and it avoids certain kinds of bugs.

I disagree.  For 16 entries, its very easy to verify by inspection and more importantly, its
perfectly clear what the result is going to be.

my %hex_conv = (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');

You must be kidding me if you think this is less error prone than my simple mapping above.
Two calls to sprintf inside a call to map!  That sounds like much more of an invitation for
something to go wrong and everybody who reads it has to do more work parsing what the heck
its doing to make sure the mappings are correct.

For fun, I printed out the result of your code:

0       0
1       1
2       10
3       11
4       100
5       101
6       110
7       111
8       1000
9       1001
a       1010
b       1011
c       1100
d       1101
e       1110
f       1111

See!  :-)  That's not going to work.  But its really hard to tell from your code.

John Black

John Black

## Re: convert a hex string to binary equivalent

[...]

I almost always use the default variable: It's a lot more clear what is
going on since use of \$_ or even absence of any variable ascertains
that "yes, we are dealing with 'the loop variable'" and it avoids
certain kinds of bugs, eg,

for my \$digt (0 .. 9) {
print(\$digit);
}

NB: I don't claim that my text makes more sense then yours, rather that
the argument doesn't make any.

Absolutely not: It's a single line of text instead of 16 lines of text
and an algorithm generating a hash instead of writing down all the
contents. Something which consists of 16 parts a lot more complicated
then something which has only one and typing the sixteen lines means
there are a lot more chances for typos.

[...]

So incredibly hard that you noticed it immediately. If 4-digit binary
numbers are to be generated, it needs to be

my %nibbles = map { sprintf('%x', \$_), sprintf('%04b', \$_) } 0 .. 15;

## Re: convert a hex string to binary equivalent

says...

I actually didn't notice it.  I pasted your code into a temp script and ran it.  The point
(and irony) was that you claimed your way was better because it was less error prone.  I
still disagree.  If my code had errors, it would have been obvious at a glance.  When yours
had errors, it was not.  That's why I still like my way but since there is no absolute right
and wrong here, let's just say we have differing tastes or styles.

John Black

## Re: convert a hex string to binary equivalent

On 30/10/2015 21:11, John Black wrote:
but since there is no absolute right

If you define objective criteria like "faster execution" then there is
right and wrong. Which are criteria are ojective ? The ones that are not
tied to our notions.

John Black

## Re: convert a hex string to binary equivalent

On 10/29/2015 03:17 PM, Rainer Weikusat wrote:

Is there anything in this module that would help?

http://search.cpan.org/~stephen/Convert-BinHex/lib/Convert/BinHex.pm

## Re: convert a hex string to binary equivalent

"BinHex is a format used by Macintosh for transporting Mac files safely
through electronic mail, as short-lined, 7-bit, semi-compressed data
streams. Ths module provides a means of converting those data streams
back into into binary data."

... and the format uses uuencoding ...

## Re: convert a hex string to binary equivalent

On 10/29/2015 03:17 PM, Rainer Weikusat wrote:

Hi rainer,

Perhaps something along the lines of what John said (a
look up table)?

-T

<code>
#!/usr/bin/perl

use strict;
use warnings;
use diagnostics;

my %LookupTable = (
'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',
);

my \$HexStr    = 'DEB328A2C4AC4DA6E46B13E38BC83F';
# my \$HexStr    = 'DEBQ328A2C4AC4DA6E46B13E38BC83F';  # note the "Q" in
the forth position
# my \$HexStr    = 'DF';
my \$BinStr    = '';
my \$Pos       = '';
my \$HexStrLen = '';
my \$Digit;

\$HexStrLen = length(\$HexStr);
for ( \$Pos = 0; \$Pos < \$HexStrLen; \$Pos += 1  ) {
\$Digit = substr ( \$HexStr, \$Pos, 1 );
\$Digit = uc ( \$Digit );
if ( \$Digit !~ m ) { die "\$Digit is not a hex digit"; }
\$BinStr .= \$LookupTable { \$Digit };
}

print "\$HexStr = \$HexStr\n";
print "\$BinStr = \$BinStr\n"
</code>

\$ HexStr2BinStr.pl

\$HexStr = DEB328A2C4AC4DA6E46B13E38BC83F
\$BinStr =
110111101011001100101000101000101100010010101100010011011010011011100100011010110001001111100011100010111100100000111111

## Re: convert a hex string to binary equivalent

On Saturday, 31 October 2015 02:35:54 UTC+5:30, T  wrote:

You can do away with the C-style for loops and also since you anyway have
a hash (lookuptable) you can check for an invalid hex via it's existence
in the hash keys.

<code>
use strict;
use warnings;

use constant EMPTY_STR => q{};
use constant VALID_HEX => sprintf('%X' x 16, 0 .. 15);
use constant ERROR_MSG => 'Valid hex digits=[' . VALID_HEX . ']';

my %Bin4Hex = (
'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',
);

#my \$HexStr    = 'DEB328A2C4AC4DA6E46B13E38BC83F';
my \$HexStr    = 'DEBQ328A2C4AC4DA6E46B13E38BC83F';  # note the "Q"...

my \$BinStr;
my \$Pos;

while ((my \$Digit = uc(substr(\$HexStr, \$Pos++, 1))) ne EMPTY_STR) {
die "'\$Digit' is not a hex digit\n", ERROR_MSG
if ! exists \$Bin4Hex{ \$Digit };

\$BinStr .= \$Bin4Hex{ \$Digit };
}

=pod
\$HexStrLen = length(\$HexStr);
for ( \$Pos = 0; \$Pos < \$HexStrLen; \$Pos += 1  ) {
\$Digit = substr ( \$HexStr, \$Pos, 1 );
\$Digit = uc ( \$Digit );
if ( \$Digit !~ m ) { die "\$Digit is not a hex digit"; }
\$BinStr .= \$LookupTable { \$Digit };
}
=cut

print "\$HexStr = \$HexStr\n";
print "\$BinStr = \$BinStr\n";

</code>

## Re: convert a hex string to binary equivalent

On 10/30/2015 10:04 PM, sharma__r@hotmail.com wrote:

Huh.

Argument "" isn't numeric in substr at ./HexStr2BinStr.pl line 44 (#1)

44: while ((my \$Digit = uc(substr(\$HexStr, \$Pos++, 1))) ne EMPTY_STR) {

## Re: convert a hex string to binary equivalent

On 11/01/2015 08:52 PM, T wrote:

44: while ((my \$Digit = uc(substr(\$HexStr, \$Pos++, 1))) ne EMPTY_STR) {
45: print "\$Pos\n";

\$ HexStr2BinStr.pl
Argument "" isn't numeric in substr at ./HexStr2BinStr.pl line 44 (#1)
(W numeric) The indicated string was fed as an argument to an operator
that expected a numeric value instead.  If you're fortunate the message
will identify which operator was so unfortunate.

1
2
3
4
...

So it is the first time through