如何将十六进制解码为Perl中的单独位?

时间:2016-11-17 13:49:56

标签: perl flags

存储为两个十六进制数字的字节包含一组标志。我需要提取这些标志,如0和1。使用以下命令迭代文件中的条目

foreach(<>)
{
@line = split(/ /,$_);

$torR = !!((hex $line[4]) & 0x3);  # bit 0 or 1
$torY = !!((hex $line[4]) & 0x4);  # bit 2
$torG = !!((hex $line[4]) & 0x8);  # bit 3

print "$torR,$torY,$torG\n";
}

在数据文件上运行:

796.129 [1f/01] len:7<  02 01 D5 01 8B 0A 8E
796.224 [1f/01] len:7<  02 01 D4 03 09 A9 B8
796.320 [1f/01] len:7<  00 01 D4 03 07 49 5A
796.415 [1f/01] len:7<  00 01 D4 00 11 A0 EE
796.515 [1f/01] len:7<  00 01 D4 00 00 31 4C
796.627 [1f/01] len:7<  02 01 D4 01 89 C1 FD
796.724 [1f/01] len:7<  02 01 D3 03 06 39 FD
796.820 [1f/01] len:7<  08 01 D4 03 08 40 6F
796.915 [1f/01] len:7<  08 01 D5 00 13 3D A4
797.015 [1f/01] len:7<  08 01 D4 00 00 34 04

实际结果 -

1,,
1,,
,,
,,
,,
1,,
1,,
,,1
,,1
,,1

期望的结果:

1,0,0
1,0,0
0,0,0
0,0,0
0,0,0
1,0,0
1,0,0
0,0,1
0,0,1
0,0,1

似乎'false'被存储为空字符串而不是'0'。

有没有一个巧妙的技巧可以立即解决这个问题,还是我需要“手动”将空字符串转换为零?

2 个答案:

答案 0 :(得分:2)

如果您希望true / false值为数字,则需要将它们强制为数字:

$torR = 0 + !!((hex $line[4]) & 0x3);  # bit 0 or 1
$torY = 0 + !!((hex $line[4]) & 0x4);  # bit 2
$torG = 0 + !!((hex $line[4]) & 0x8);  # bit 3

请记住,空字符串''也是假值。

另一方面,我可能倾向于将其写为:

my (@ryg) = map 0 + !!((hex $line[4]) & $_), 0x3, 0x4, 0x5;
print join(', ', @ryg), "\n";

此外,您可能会因为在程序中不使用普通数字而受益。例如,考虑使用%FLAG结构为这些常量提供名称,并使用%COL结构为您感兴趣的列提供名称。使用您发布的数据:

use Const::Fast;

const my %FLAG => (
    torR => 0x3,
    torY => 0x4,
    torG => 0x5,
);

const my %COL => (
    # ...
    tor => 4,
);

while (my $line = <DATA>) {
    my @line = split ' ', $line;
    my %set_flags = map +($_ => 0 + !!((hex $line[$COL{tor}]) & $FLAG{$_})), qw(torR torY torG);
    print join(', ', @set_flags{qw(torR torY torG)}), "\n";
}

__DATA__
796.129 [1f/01] len:7<  02 01 D5 01 8B 0A 8E
796.224 [1f/01] len:7<  02 01 D4 03 09 A9 B8
796.320 [1f/01] len:7<  00 01 D4 03 07 49 5A
796.415 [1f/01] len:7<  00 01 D4 00 11 A0 EE
796.515 [1f/01] len:7<  00 01 D4 00 00 31 4C
796.627 [1f/01] len:7<  02 01 D4 01 89 C1 FD
796.724 [1f/01] len:7<  02 01 D3 03 06 39 FD
796.820 [1f/01] len:7<  08 01 D4 03 08 40 6F
796.915 [1f/01] len:7<  08 01 D5 00 13 3D A4
797.015 [1f/01] len:7<  08 01 D4 00 00 34 04

答案 1 :(得分:1)

我想我会使用splitunpack将每个值转换为零和1的数组,然后单独检查它们

use strict;
use warnings 'all';

for my $val ( qw/ 02 02 00 00 00 01 01 08 08 08 / ) {

    my @bits = split //, unpack 'b8', chr hex $val;

    my $torR = $bits[0] || $bits[1] ? 1 : 0;
    my $torY = $bits[2] ? 1 : 0;
    my $torG = $bits[3] ? 1 : 0;

    print "$torR,$torY,$torG\n";
}

输出

1,0,0
1,0,0
0,0,0
0,0,0
0,0,0
1,0,0
1,0,0
0,0,1
0,0,1
0,0,1

或者使用产生相同结果的Bit::Vector的方法

use strict;
use warnings 'all';

use Bit::Vector;

for my $val ( qw/ 02 02 00 00 00 01 01 08 08 08 / ) {

    my $vec = Bit::Vector->new_Hex(8, $val);

    my $torR = $vec->Chunk_Read(2, 0) ? 1 : 0;
    my $torY = $vec->Chunk_Read(1, 2) ? 1 : 0;
    my $torG = $vec->Chunk_Read(1, 3) ? 1 : 0;

    print "$torR,$torY,$torG\n";
}