我有一项业务要求,要读取字节长度在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字节的带符号值吗?
答案 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" ) )