perl - анализировать целое число со знаком для длины байта 3,5,6,7 - PullRequest
2 голосов
/ 27 марта 2019

У меня есть бизнес-требование прочитать целочисленные значения со знаком с длиной байтов от 1 до 8.

Стандартная реализация значений int занимает байты порядка 2 ** n

> perl -e ' $x=chr(253); $y=unpack "c",$x; printf("%d\n",$y) ' # 1 byte
-3
> perl -e ' $x=chr(255).chr(253); $y=unpack "s>",$x; printf("%d\n",$y)' # 2 byte
-3
> perl -e ' $x=chr(255) x 3;$x.=chr(253); $y=unpack "i>",$x; printf("%d\n",$y) ' # 4 byte
-3
> perl -e ' $x=chr(255) x 7;$x.=chr(253); $y=unpack "q>",$x; printf("%d\n",$y) ' # 8 byte
-3
>

Для 3, 5, 6, 7 байтов я пытаюсь как показано ниже

> perl -e ' $x=chr(255) x 2;$x.=chr(253); $y=unpack "i>",$x; printf("%d\n",$y) '
0
>

Но это неправильно, мне нужно -3.

Эта ссылка Декодирование3-байтовое целое число в Perl отвечает за числа без знака, но не решает мою проблему.

Может ли кто-нибудь помочь получить значения со знаком для 3, 5, 6, 7 байтов?

1 Ответ

5 голосов
/ 27 марта 2019

Чтобы увеличить размер целого числа из 2-х, необходимо использовать расширение знака .Это означает, что вам нужно скопировать знаковый бит числа в каждый добавляемый вами бит.

  +---+---+---+---+---+---+---+------+
  |   |   |   |   |   |   |   |      |
  v   v   v   v   v   v   v   v      |
+---+---+---+---+---+---+---+---+  +---+---+---+---+---+---+---+---+--
|   |   |   |   |   |   |   |   |  |   |   |   |   |   |   |   |   | ...
+---+---+---+---+---+---+---+---+  +---+---+---+---+---+---+---+---+--
MSB         new byte          LSB  MSB        old byte(s)        LSB

Таким образом, 0000 0011 (3) становится 0000 0000 0000 0011 (3)
И 1111 1101 (-3) становится 1111 1111 1111 1101 (-3).

Общие решения:

  • Чтобы распаковать 1 - 8-байтовое число с прямым порядком байтов:

    unpack( "q>", substr( ( ord($_) & 0x80 ? "\xFF"x7 : "\x00"x7 ) . $_, -8 ) )
    

    Тест:

    $ perl -e'
       for ( map { ( "\x00" x $_ ) . "\x03", ( "\xFF" x $_ ) . "\xFD" } 0..7 ) {
          printf "%v02X => %d\n",
             $_, unpack( "q>", substr( ( ord($_) & 0x80 ? "\xFF"x7 : "\x00"x7 ) . $_, -8 ) );
       }
    '
    03 => 3
    FD => -3
    00.03 => 3
    FF.FD => -3
    00.00.03 => 3
    FF.FF.FD => -3
    00.00.00.03 => 3
    FF.FF.FF.FD => -3
    00.00.00.00.03 => 3
    FF.FF.FF.FF.FD => -3
    00.00.00.00.00.03 => 3
    FF.FF.FF.FF.FF.FD => -3
    00.00.00.00.00.00.03 => 3
    FF.FF.FF.FF.FF.FF.FD => -3
    00.00.00.00.00.00.00.03 => 3
    FF.FF.FF.FF.FF.FF.FF.FD => -3
    
  • Для распаковки порядкового номера от 1 до 8 байтов:

    unpack( "q<", $_ . ( ord(substr($_, -1)) & 0x80 ? "\xFF"x7 : "\x00"x7 ) )
    

    Тест:

    $ perl -e'
       for ( map { "\x03" . ( "\x00" x $_ ), "\xFD" . ( "\xFF" x $_ ) } 0..7 ) {
          printf "%v02X => %d\n",
             $_, unpack( "q<", $_ . ( ord(substr($_, -1)) & 0x80 ? "\xFF"x7 : "\x00"x7 ) );
       }
    '
    03 => 3
    FD => -3
    03.00 => 3
    FD.FF => -3
    03.00.00 => 3
    FD.FF.FF => -3
    03.00.00.00 => 3
    FD.FF.FF.FF => -3
    03.00.00.00.00 => 3
    FD.FF.FF.FF.FF => -3
    03.00.00.00.00.00 => 3
    FD.FF.FF.FF.FF.FF => -3
    03.00.00.00.00.00.00 => 3
    FD.FF.FF.FF.FF.FF.FF => -3
    03.00.00.00.00.00.00.00 => 3
    FD.FF.FF.FF.FF.FF.FF.FF => -3
    

Специальные решения:

  • Для распаковки 3-байтового числа с прямым порядком байтов:

    unpack( "l>", ( ord($_) & 0x80 ? "\xFF" : "\x00" ) . $_ )
    
  • Чтобы распаковать 3-байтовое число с прямым порядком байтов:

    unpack( "l<", $_ . ( ord(substr($_, -1)) & 0x80 ? "\xFF" : "\x00" ) )
    
  • Чтобы распаковать 5-байтовое число с прямым порядком байтов:

    unpack( "q>", ( ord($_) & 0x80 ? "\xFF"x3 : "\x00"x3 ) . $_ )
    
  • Чтобы распаковать 5-байтовый номер с прямым порядком байтов:

    unpack( "q<", $_ . ( ord(substr($_, -1)) & 0x80 ? "\xFF"x3 : "\x00"x3 ) )
    
  • Чтобы распаковать 6-байтовый номер с прямым порядком байтов:

    unpack( "q>", ( ord($_) & 0x80 ? "\xFF"x2 : "\x00"x2 ) . $_ )
    
  • Чтобы распаковать 6-байтовый номер с прямым порядком байтов:

    unpack( "q<", $_ . ( ord(substr($_, -1)) & 0x80 ? "\xFF"x2 : "\x00"x2 ) )
    
  • Чтобы распаковать 7-байтовый номер с прямым порядком байтов:

    unpack( "q>", ( ord($_) & 0x80 ? "\xFF" : "\x00" ) . $_ )
    
  • Для распаковки 7-ми байт, маленький-еИндийский номер:

    unpack( "q<", $_ . ( ord(substr($_, -1)) & 0x80 ? "\xFF" : "\x00" ) )
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...