perl - perl - 解析字节长度为 3,5,6,7 的有符号整数
问题描述
我有一个业务需求来读取字节长度从 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 字节的有符号值吗?
解决方案
要扩展 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 字节的 little-endian 数字:
unpack( "l<", $_ . ( ord(substr($_, -1)) & 0x80 ? "\xFF" : "\x00" ) )
解压一个 5 字节的 big-endian 数字:
unpack( "q>", ( ord($_) & 0x80 ? "\xFF"x3 : "\x00"x3 ) . $_ )
解压一个 5 字节的 little-endian 数字:
unpack( "q<", $_ . ( ord(substr($_, -1)) & 0x80 ? "\xFF"x3 : "\x00"x3 ) )
解压一个 6 字节的 big-endian 数字:
unpack( "q>", ( ord($_) & 0x80 ? "\xFF"x2 : "\x00"x2 ) . $_ )
解压一个 6 字节的 little-endian 数字:
unpack( "q<", $_ . ( ord(substr($_, -1)) & 0x80 ? "\xFF"x2 : "\x00"x2 ) )
解压一个 7 字节的大端数字:
unpack( "q>", ( ord($_) & 0x80 ? "\xFF" : "\x00" ) . $_ )
解压一个 7 字节的 little-endian 数字:
unpack( "q<", $_ . ( ord(substr($_, -1)) & 0x80 ? "\xFF" : "\x00" ) )
推荐阅读
- sql - 将一个值插入到包含另一个表中的所有值的表中
- angular - 对私有 npm 注册表的 npm 审计
- visual-studio - 如何从 Powershell 7 将文件添加到 SSDT Visual Studio 项目
- python - standard_init_linux.go:228:exec 用户进程导致:使用 chmod 时出现 exec 格式错误
- algorithm - 如果您为每个已删除的边计算无法相互访问的节点对
- python - 如何使用 Flask 在 Python 中调用包内的模块?
- java - 具有类返回类型的方法
- android - 谷歌成就解锁但不保存
- verilog - If 语句中的 verilog 错误。(reg) 不是常数。目标
并发分配或输出端口连接应该是网络类型 - java - 如果 Spring Data JPA 中的参数为空,则忽略条件