perl-解析有符号整数,字节长度为3、5、6、7

时间:2019-03-26 21:30:23

标签: perl

我有一项业务要求,要读取字节长度在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。

此链接Decoding 3-byte integer in Perl回答未签名的数字,但不能解决我的问题。

有人可以帮助获取3、5、6、7字节的带符号值吗?

1 个答案:

答案 0 :(得分:5)

要扩展2的补码整数的大小,必须使用sign extension。这意味着您需要将数字的符号位复制到添加的每个位中。

  +---+---+---+---+---+---+---+------+
  |   |   |   |   |   |   |   |      |
  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" ) )