Распаковка Perl Pack и обратный порядок байтов

Я пытаюсь написать скрипт для поиска шестнадцатеричных строк в текстовом файле и преобразования их в обратный порядок байтов. У меня проблема в том, что некоторые из шестнадцатеричных строк 16-битные, а некоторые - 64-битные. Я использовал пакет Perl для упаковки и распаковки 16-битных шестнадцатеричных чисел, и это работает нормально, а 64-битные - нет.

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

Я пробовал второй с q и Q (где я получаю ffffffffffffffff). Я подхожу ко всему этому неправильно?

В качестве предыстории у меня есть текстовый файл с разделителями в несколько гигабайт, в котором есть шестнадцатеричные строки в обратном порядке байтов, как описано выше. Также столбцы файла не стандартны; иногда шестнадцатеричные строки появляются в одном столбце, а иногда и в другом. Мне нужно преобразовать шестнадцатеричные строки в их обратный порядок байтов.


person user3602852    schedule 05.05.2014    source источник


Ответы (2)


Всегда use warnings;. Если вы это сделаете, вы получите следующее сообщение:

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

Их можно устранить, используя use bigint; и изменив объявление второго числа на hex('0x4f423230313430343239303030636334').

Однако это число все еще слишком велико, чтобы pack 'I' мог его обработать.

Возможно, это можно сделать с помощью простых манипуляций со строками:

use strict;
use warnings;

my @nums = qw(
    0x20202032
    0x4f423230313430343239303030636334
);

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

__END__

Выходы:

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

Или для обработки цифр нечетной длины:

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"
person Miller    schedule 05.05.2014
comment
Это сработало отлично! Спасибо. Извините, я немного новичок в perl. Будет ли лучший способ применить это как замену регулярного выражения в виде простой подпрограммы, которая запускается для совпадений в каждой строке с помощью переключателя «e»? - person user3602852; 05.05.2014
comment
perl -n -i reverse_it.pl мой файл.txt. Добавьте флаг g в конце команды s{}{}. reverse_it.pl содержит последний сегмент кода выше. - person Colin; 05.05.2014

Чтобы быть педантичным, шестнадцатеричные числа в вашем примере имеют длину 32 и 128 бит, а не 16 и 64. Если бы самое длинное число было только 64-битным, вы могли бы успешно использовать шаблон Q pack, как вы предполагали (при условии, что шляпа ваш Perl был скомпилирован для поддержки 64-битных целых чисел).

Решение pack/unpack можно использовать в любом случае (если с добавлением reverse - вам также нужно удалить ведущее 0x из шестнадцатеричных строк или обрезать последние два символа из результатов):

print unpack "H*", reverse pack "H*", $hex_string;

Пример с вашими значениями:

perl -le 'print unpack "H*", reverse pack "H*", "4f423230313430343239303030636334"'
3463633030303932343034313032424f
person emazep    schedule 05.05.2014