【问题标题】:How to decode hex to negative integer value in Perl (Part 2)如何在 Perl 中将十六进制解码为负整数值(第 2 部分)
【发布时间】:2020-04-14 18:25:53
【问题描述】:

我对十六进制格式的负整数值有疑问。我的工作有 2 种格式,如下例所示:

###########################################################################

=for comment

B4 80 B5 FF 07 =  2146254900
B5 80 B5 FF 07 =  2146254901

A9 B4 E8 8C 08 = -2120607191
AA B4 E8 8C 08 = -2120607190

A3 86 8D 8E FC FF FF FF FF 01 = -1044167901
A4 86 8D 8E FC FF FF FF FF 01 = -1044167900

=cut

###########################################################################

$vars[0] = "\xB4\x80\xB5\xFF\x07";
$vars[1] = "\xB5\x80\xB5\xFF\x07";
$vars[2] = "\xA9\xB4\xE8\x8C\x08";
$vars[3] = "\xAA\xB4\xE8\x8C\x08";
$vars[4] = "\xA3\x86\x8D\x8E\xFC\xFF\xFF\xFF\xFF\x01";
$vars[5] = "\xA4\x86\x8D\x8E\xFC\xFF\xFF\xFF\xFF\x01";

###########################################################################

for my $var (@vars)
{
    print decodeInt($var) . "\n";
}

###########################################################################

sub decodeInt
{
    $_[0] =~ s/^([\x80-\xFF]*[\x00-\x7F])// or return;

    my $encoded_num = $1;
    my $num = 0;

    for ( reverse unpack 'C*', $encoded_num )
    {
        $num = ( $num << 7 ) | ( $_ & 0x7F );
    }

    $num = unpack 'q', pack 'Q', $num;  #Cast two unsigned complement Ints back to 64-bit signed

    return $num;
}

##########################################################################

以前,上面的代码运行良好。但是现在我有了新的负整数值格式,它不起作用。

A9 B4 E8 8C 08 = -2120607191
AA B4 E8 8C 08 = -2120607190

我的代码解码为 2174360105 和 2174360106。

【问题讨论】:

  • 看起来像 2174360105 - 2^32 = -2120607191
  • 再添加一个例子:A1 E1 CE FD 0F = -5001055 不知道怎么判断是不是负值。
  • Re“我不知道如何检查它是否为负值。”,从你显示的数据中无法判断。
  • 顺便说一下,你的 decodeInt() 函数修改了它的参数,这通常被认为是一件坏事。这似乎也没有必要。你可以只做一个匹配 m// 而不是替换 s///。
  • @Dave Mitchell,我相信这里说得通。有 BER 编码的数字,它是流的可变长度编码。因此,解码器/提取器从输入中删除编码数字是有意义的,以便可以从编码数字停止的地方继续提取。

标签: perl integer hex


【解决方案1】:
$num = unpack 'q', pack 'Q', $num;

适用于 64 位有符号整数。对于 32 位值,您需要使用

$num = unpack 'l', pack 'L', $num;

$num -= 2**32;

【讨论】:

  • 我相信 $num -= (1 的计算效率会更高。
  • @Polar Bear,我使用幂运算而不是移位,因此它适用于 32 位构建。忽略这一点,我们谈论的是编译时的一次性评估,因此差异并不重要。即使它没有在编译时发生,与开销相比,差异也相对较小。不过,我确实切换到了=-
【解决方案2】:

也许下面的代码会转换成想要的结果

use strict;
use warnings;

use feature 'say';

use Data::Dumper;

my $debug = 0;

while( my $num = <DATA> ) {
    chomp $num;
    say hex2dec($num);
}

sub hex2dec {
    my $data = shift;

    my $sign    = 0;
    my $num     = 0;
    my $bits;

    my @data = reverse split ' ', $data;

    $bits = 32 if scalar @data == 5;
    $bits = 64 if scalar @data == 10;

    map { $num = ($num << 7)|h2i($_) } @data;

    $sign = ($num & (1<<($bits-1)) ) ? 1 : 0;

    if( $debug ) {
        say Dumper(\@data);
        say "bits : $bits";
        say "sign : $sign";
        say "num  : $num";
        num2bin( $num,$bits);
        num2bin(~$num,$bits);
    }

    if( $sign ) {
        $num = -1 * ((1<<$bits) - $num) if $bits == 32;
        $num = -1 * (~$num + 1)         if $bits == 64;
    }

    return $num;
}

# Hex byte to integer number
sub h2i {
    my $data = shift;

    my $r = 0;
    my %hex2dec;

    @hex2dec{0..9,'A'..'F'} = 0..15; 

    map { $r <<= 4; $r += $hex2dec{$_} } split '', $data;

    return $r&0x7F;
}

# Print integer number as bin 
sub num2bin {
    my $num = shift;
    my $bit = shift;

    my $bin;

    while( $bit-- ) {
        $bin .= $num & (1<<$bit) ? '1' : '0';
    }

    say $bin;
}

=for comment

B4 80 B5 FF 07 =  2146254900
B5 80 B5 FF 07 =  2146254901

A9 B4 E8 8C 08 = -2120607191
AA B4 E8 8C 08 = -2120607190

A1 E1 CE FD 0F = -5001055

A3 86 8D 8E FC FF FF FF FF 01 = -1044167901
A4 86 8D 8E FC FF FF FF FF 01 = -1044167900

=cut

__DATA__
B4 80 B5 FF 07
B5 80 B5 FF 07
A9 B4 E8 8C 08
AA B4 E8 8C 08
A1 E1 CE FD 0F
A3 86 8D 8E FC FF FF FF FF 01
A4 86 8D 8E FC FF FF FF FF 01

输出

2146254900
2146254901
-2120607191
-2120607190
-5001055
-1044167901
-1044167900

【讨论】:

    【解决方案3】:

    代码的其他变体

    use strict;
    use warnings;
    
    use feature 'say';
    
    use bytes;
    
    my $debug = 1;
    
    my @vars = ( "\xB4\x80\xB5\xFF\x07",
                 "\xB5\x80\xB5\xFF\x07",
                 "\xA9\xB4\xE8\x8C\x08",
                 "\xAA\xB4\xE8\x8C\x08",
                 "\xA1\xE1\xCE\xFD\x0F",
                 "\xA3\x86\x8D\x8E\xFC\xFF\xFF\xFF\xFF\x01",
                 "\xA4\x86\x8D\x8E\xFC\xFF\xFF\xFF\xFF\x01"
                );
    
    foreach my $v (@vars) {
        say 'DATA   : ' 
            . decode($v)
            . '  :: '
            . unpack 'H'. bytes::length($v)*2, $v;
    }
    
    sub decode {
        my $data = shift;
    
        my $num = 0;
        my $bits;
        my $ret;
    
        map { $num = ($num << 7) | ($_ & 0x7F) } reverse unpack 'C*', $data;
    
        my $bytes = bytes::length($num);
    
        $bits = 32 if $bytes == 10;
        $bits = 64 if $bytes == 20;
    
        my $sign = $num & (1<<($bits-1)) ? 1 : 0;
    
        if( $sign ) {
            $ret = -1 * ((1<<$bits) - $num) if $bits == 32;
            $ret = -1 * (~$num + 1)         if $bits == 64;
        } else {
            $ret = $num;
        }
    
        if( $debug ) {
            say "\nDebug\n" . '-' x 20;
            say "data   : " . unpack 'H'. $bytes, $data;
            say "bytes  : $bytes";
            say "num    : $num (dec)";
            say "bits   : $bits";
            say "sign   : $sign";
            say "hex    : " . itoh($num);
            say "bin    : " . num2bin( $num, $bits);
            say "~bin   : " . num2bin(~$num, $bits);
            say "decode : $ret";
        }
    
        return $ret;
    }
    
    # Print integer number as bin 
    sub num2bin {
        my $num = shift;
        my $bit = shift;
    
        my $bin;
    
        while( $bit-- ) {
            $bin .=  $num & (1<<$bit) ? '1' : '0';
        }
    
        return $bin;
    }
    
    # Convert integer to ascii hex
    sub itoh {
        my $data = shift;
    
        my $hex;
        my %hex;
    
        @hex{0..15} = (0..9,'A'..'F');
    
        while( $data ) {
            my $n = $data & 0xF;
            $data >>= 4;
            $hex .= $hex{$n};
        }
    
        return reverse $hex;
    }
    

    输出

    Debug
    --------------------
    data   : b480b5ff07
    bytes  : 10
    num    : 2146254900 (dec)
    bits   : 32
    sign   : 0
    hex    : 7FED4034
    bin    : 01111111111011010100000000110100
    ~bin   : 10000000000100101011111111001011
    decode : 2146254900
    DATA   : 2146254900  :: b480b5ff07
    
    Debug
    --------------------
    data   : b580b5ff07
    bytes  : 10
    num    : 2146254901 (dec)
    bits   : 32
    sign   : 0
    hex    : 7FED4035
    bin    : 01111111111011010100000000110101
    ~bin   : 10000000000100101011111111001010
    decode : 2146254901
    DATA   : 2146254901  :: b580b5ff07
    
    Debug
    --------------------
    data   : a9b4e88c08
    bytes  : 10
    num    : 2174360105 (dec)
    bits   : 32
    sign   : 1
    hex    : 819A1A29
    bin    : 10000001100110100001101000101001
    ~bin   : 01111110011001011110010111010110
    decode : -2120607191
    DATA   : -2120607191  :: a9b4e88c08
    
    Debug
    --------------------
    data   : aab4e88c08
    bytes  : 10
    num    : 2174360106 (dec)
    bits   : 32
    sign   : 1
    hex    : 819A1A2A
    bin    : 10000001100110100001101000101010
    ~bin   : 01111110011001011110010111010101
    decode : -2120607190
    DATA   : -2120607190  :: aab4e88c08
    
    Debug
    --------------------
    data   : a1e1cefd0f
    bytes  : 10
    num    : 4289966241 (dec)
    bits   : 32
    sign   : 1
    hex    : FFB3B0A1
    bin    : 11111111101100111011000010100001
    ~bin   : 00000000010011000100111101011110
    decode : -5001055
    DATA   : -5001055  :: a1e1cefd0f
    
    Debug
    --------------------
    data   : a3868d8efcffffffff01
    bytes  : 20
    num    : 18446744072665383715 (dec)
    bits   : 64
    sign   : 1
    hex    : FFFFFFFFC1C34323
    bin    : 1111111111111111111111111111111111000001110000110100001100100011
    ~bin   : 0000000000000000000000000000000000111110001111001011110011011100
    decode : -1044167901
    DATA   : -1044167901  :: a3868d8efcffffffff01
    
    Debug
    --------------------
    data   : a4868d8efcffffffff01
    bytes  : 20
    num    : 18446744072665383716 (dec)
    bits   : 64
    sign   : 1
    hex    : FFFFFFFFC1C34324
    bin    : 1111111111111111111111111111111111000001110000110100001100100100
    ~bin   : 0000000000000000000000000000000000111110001111001011110011011011
    decode : -1044167900
    DATA   : -1044167900  :: a4868d8efcffffffff01
    

    【讨论】:

      猜你喜欢
      • 2020-04-07
      • 1970-01-01
      • 2011-12-10
      • 2019-04-30
      • 2012-08-04
      • 2013-10-30
      • 1970-01-01
      • 2021-09-29
      相关资源
      最近更新 更多