Gaax Gaax - 5 months ago 16
Perl Question

How do I output a string of hex values into a binary file in Perl?

I'm trying to read in a binary file, store the data as a string of hex values, use regular expressions to modify some hex values, and spit the data out into a new binary file (that is a slightly modified version of the original).

I can do all of those steps except for the last one. When I open the new binary file in the hex editor, it all seems to be wrong... I want it to be exactly how my hex string is, but in the hex editor.

Here is what I'm attempting to do when creating the new file:

# Format data string into an array for file processing
@data_ary = unpack('H*', $data_str);

generate_new_file($filname, \@data_ary);


sub generate_new_file
{
my $fname = "mod -" . shift(@_);
my $aref = shift(@_);

open(BIN, ">", $fname) or die;
binmode(BIN);

for my $nybble(@$aref)
{
print (BIN $nybble)
}
close(BIN);
}


I'm guessing my problem has to do with my use of unpack. But I'm not really sure how else to get a huge hex string into a form where it will actually be read as hex and not ASCII characters. Any suggestions are greatly appreciated!

The code shown above is only for trying to output the data into a new file. I already have all the hex I want to output in $data_str. So the unpack is an attempt to get the string of hex into a list of hex values.

I'm getting closer. I removed the unpack from the beginning since my data is already a single string of hex. So I just split it and put it into the array. This at least gets the size of the file correct now. However, my new problem is that it's cropping off the second part of every byte and replacing it with a 0 (when viewed in the hex editor)... But when I print, the elements of the array get the correct data. Any ideas? New code below:

# Format data string into an array for file processing
@data_ary = split //, $data_str;
generate_new_file($filname, \@data_ary);


sub generate_new_file
{
my $fname = "mod -" . shift(@_);
my $aref = shift(@_);

open(BIN, ">", $fname) or die;
binmode(BIN);

for (my $i = 0; $i < @$aref; $i += 2)
{
my ($hi, $lo) = @$aref[$i, $i+1];
print BIN pack "H", $hi.$lo;
}

close(BIN);
}


I figured it out! I forgot the "*" when calling pack, so it would do more than just the first character! The finished code is below. Thanks Amon!

# Format data string into an array for file processing
@data_ary = split //, $data_str;
generate_new_file($filname, \@data_ary);


sub generate_new_file
{
my $fname = "mod -" . shift(@_);
my $aref = shift(@_);

open(BIN, ">", $fname) or die;
binmode(BIN);

for (my $i = 0; $i < @$aref; $i += 2)
{
my ($hi, $lo) = @$aref[$i, $i+1];
print BIN pack "H*", $hi.$lo;
}
close(BIN);
}

Answer

Here, the unpack returns a single string, not an array of values. If you want to have an array of hex characters (each denoting 4 bits), then you have to split the resulting string:

my @data = split //, unpack "H*", $data;

(use split /..\K/, $data to split into byte-equivalents)

Before printing this data to a filehandle, you also have to pack it to get the original data again. I would recommend to do this at least on 8-bit parts of the original data:

for (my $i = 0; $i < @$aref; $i += 2) {
   my ($hi, $lo) = @$aref[$i, $i+1];
   print OUT pack "H*", $hi.$lo;
}