perlunpack

Pack Unpack and Reverse Byte Order


I'm trying to write a script to find hex strings in a text file and convert them to their reverse byte order. The trouble I'm having is that some of the hex strings are 16-bit and some are 64 bits. I've used Perl's pack to pack and unpack the 16-bit hex numbers and that works fine, but the 64-bit does not.

print unpack("H*", (pack('I!', 0x20202032))). "\n"; #This works, gives 32202020
#This does not
print unpack("H*", (pack('I!', 0x4f423230313430343239303030636334))). "\n";

I've tried the second with the q and Q (where I get ffffffffffffffff). Am I approaching this all wrong?

As bit of background, I've got a multi-gigabyte pipe-delimited text file that has hex strings in reverse byte order as explained above. Also, the columns of the file are not standard; sometimes the hex strings appear in one column, and sometimes in another. I need to convert the hex strings to their reverse byte order.


Solution

  • Always use warnings;. If you do, you'll get the following message:

    Integer overflow in hexadecimal number at scratch.pl line 8.
    Hexadecimal number > 0xffffffff non-portable at scratch.pl line 8.
    

    These can be resolved by use bigint; and by changing your second number declaration to hex('0x4f423230313430343239303030636334').

    However, that number is still too large for pack 'I' to be able to handle.

    Perhaps this can be done using simple string manipulation:

    use strict;
    use warnings;
    
    my @nums = qw(
        0x20202032
        0x4f423230313430343239303030636334
    );
    
    for (@nums) {
        my $rev = join '', reverse m/([[:xdigit:]]{2})/g;
        print "$_ -> 0x$rev\n"
    }
    
    __END__
    

    Outputs:

    0x20202032 -> 0x32202020
    0x4f423230313430343239303030636334 -> 0x3463633030303932343034313032424f
    

    Or to handle digits of non-even length:

    my $rev = $_;
    $rev =~ s{0x\K([[:xdigit:]]*)}{
        my $hex = $1;
        $hex = "0$hex" if length($hex) % 2;
        join '', reverse $hex =~ m/(..)/g;
    }e;
    print "$_ -> $rev\n"