29

I recently wrote a script which parsed a text representation of a single binary byte month field.

(Don't ask :-{ )

After fiddling with sprintf for a while I gave up and did this;

our %months = qw / x01 1 
       x02 2
       x03 3 
       x04 4 
       x05 5 
       x06 6 
       x07 7 
       x08 8 
       x09 9 
       x0a 10 
       x0b 11 
       x0c 12 /;
...
my $month = $months{$text};

Which I get away with, because I'm only using 12 numbers, but is there a better way of doing this?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chris Huang-Leaver
  • 6,059
  • 6
  • 41
  • 67

4 Answers4

42

If you have

$hex_string = "0x10";

you can use:

$hex_val = hex($hex_string);

And you'll get: $hex_val == 16

hex doesn't require the "0x" at the beginning of the string. If it's missing it will still translate a hex string to a number.

You can also use oct to translate binary, octal or hex strings to numbers based on the prefix:

  • 0b - binary
  • 0 - octal
  • 0x - hex
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nathan Fellman
  • 122,701
  • 101
  • 260
  • 319
8

See hex and/or oct.

#!/usr/bin/perl

use strict;
use warnings;

my @months = map hex, qw/x01 x02 x03 x04 x05 x06 x07 x08 x09 x0a x0b x0c/;
print "$_\n" for @months;
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
  • To expound: "say" is one of the (IMHO questionable) new bits of syntax added to recent perls. It doesn't work by default, because being new syntax it would break old scripts. So you use the '-E' argument to run a script with "all" features. But of course that gets you all features added since your script was written too, which may break it. Basically, the given example is more about showing off than it is about enginering a solution. But the answer about the hex function is right. – Andy Ross Oct 07 '09 at 17:56
  • @Andy Ross: There is no showing off. One can just use `print "$_\n"` rather than `say`. In the case of the first example, however, using print would have required me to write `print qq{$_\n}` which is obviously more cluttered. Both examples are written to be copied & pasted to the command prompt and for illustration only. There is no script to be broken. If one only wants to use `say` in one's script, one can do `use feature 'say'` or `use Perl6::Say` (available on CPAN). Finally, **what engineering solution???** – Sinan Ünür Oct 07 '09 at 18:00
  • My point was more about -E than say. The -E argument can't be reliably used for anything, as it doesn't specify a specific language, just "whatever is most recent". And I don't follow your quoting argument. You can enclose double quotes inside single quotes on a command line just fine... – Andy Ross Oct 07 '09 at 18:03
  • 1
    @Andy Ross a plain reading of both your comments leave me puzzled. But, life is too short. No more `-E`, no more `say` even though I think your criticism is vacuous. – Sinan Ünür Oct 07 '09 at 18:13
3

If I understand correctly you have 1 byte per month - not string "0x10", but rather byte with 10 in it.

In this way, you should use unpack:

my $in = "\x0a";
print length($in), "\n";
my ($out) = unpack("c", $in);
print length($out), "\n", $out, "\n"

output:

1
2
10

If the input are 3 characters, like "x05", then changing is also quite simple:

my $in = "x0a";
my $out = hex($in);
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
  • 1
    @depesz Your downvotes are simply unjustified. Do you know what the `qw//` operator does in Perl? Do you know what *a text representation of single binary byte* means? – Sinan Ünür Oct 07 '09 at 14:52
  • @Sinan: I know. I guess that OP gave \x01, and not x01 - and the \ was simply lost someplace. please note from his question: "single binary byte month field". not "4 byte string \x01" or "3 byte string" –  Oct 07 '09 at 15:14
  • @depesz **text representation** of a single binary byte month field. Words must mean something. – Sinan Ünür Oct 07 '09 at 15:17
  • @Sinan - ok. now you made me doubt. Downvotes removed accordingly. –  Oct 07 '09 at 15:19
0

Here's another way that may be more practical for directly converting the hexadecimals contained in a string.

This make use of the /e (e for eval) regex modifier on s///.

Starting from this string:

$hello_world = "\\x48\\x65\\x6c\\x6c\\x6f\\x20\\x57\\x6f\\x72\\x6c\\x64";

Hexadecimals to characters :

print $hello_world =~ s/\\x([0-9a-fA-F]{2})/chr hex $1/gre;

Hexadecimals to decimal numbers :

print $hello_world =~ s/\\x([0-9a-fA-F]{2})/hex $1/gre;

Drop the /r modifier to substitute the string in-place.

One day I used a python script that did stuff with a binary file and I was stuck with a bytes literal (b'\x09\xff...') containing only hexadecimal digits. I managed to get back my bytes with a one-liner that was a variant of the above.

WhiteMist
  • 885
  • 4
  • 13