views:

78

answers:

3

Hello,

I'm quite new to Perl development, and I'd like to perform a following task:

My script receives hex-encoded string as command-line param. Then I must decode this string and write it to output file like a C++ array with initialization from data given. For example:

perl myscript.pl DEADBABEDEADBEEF and the output something like

const boost::array<char, 8> MyArray = { 0xDE, 0xAD, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF };

What is the right way to do this with Perl regex? Of course, I could perform it in loop with substrings, but I believe that there should be more elegant way.

EDIT: the input string is of fixed length.

+4  A: 

How about this:

my $input = $ARGV[0];
die "Fouled up input" unless $input =~ /^(?:[0-9A-F]{2})+$/i;
my $bytes = length ($input) / 2;
print "const boost::array<char, $bytes> MyArray = {";
while ($input =~ s/([0-9A-F]{2})//i) {
    # print $input # to see how this works, see comment.
    print "0x$1, ";
}
print "};\n";
Kinopiko
Thanks, your answer is correct. But the second answer is correct, too. I'm not sure for whom should I vote. :)And I have a question about your code: why in statement `$input =~ s/([0-9A-F]{2})//` you left second argument of s/// empty? What does this mean in this context?
Haspemulator
It just deletes the first two characters each time. Put a `print $input` in the loop and you'll see how it works.
Kinopiko
@Kinopiko, ok, I've got it. Thank you.
Haspemulator
+1 for checking input!
drewk
+5  A: 

Try this:

my $hex = "DEADBABEDEADBEEF";
my @a = map "0x$_", $hex =~ /(..)/g;

How it works:

First, $hex =~ /(..)/g in list context captures all 2-character substrings (the /g flag means global match). Then map() takes the list and transforms it to another one, using the "0x$_" expression for each element of the first list ($_ here is an alias for the element).

See also perldoc -f map.

eugene y
Thanks, your script is working. But could you please explain me the details? What is happened there, and what is the ',' (comma) between the operation? As I said, I'm quite new to Perl. :)
Haspemulator
@Haspemulator: That's quite a tall order. Try `perldoc -f map` to get documentation for the map function, and `perldoc perlre` to get documentation for regular expressions.
Kinopiko
@Kinopiko, thanks for comment, it is more clear now.
Haspemulator
Great solution, but it might be better to limit the two digits retrieved from the string to legit hex digits. ie, `my @a = map "0x$_", $hex =~ /([\da-eA-E]{2})/g;`, no?
drewk
+3  A: 

How about unpack?

print join ",", unpack("(A2)*", "DEADBABEDEADBEEF");

Correction - you'd need a map to prefix each element that unpack returns with a "0x"

print join ",", map { '0x' . $_ } unpack("(A2)*", "DEADBABEDEADBEEF");
deepakg