读取二进制数据并将其转换为十六进制

时间:2020-09-23 23:47:58

标签: perl file-io type-conversion

我是perl的新手。我正在尝试编写从二进制文件读取数据并以指定格式返回的代码。它可以是十进制,无符号,十六进制... 我的二进制文件在十六进制编辑器中打开时看起来像这样:

7C 00 48 00 D6 00 E4 07 04 07 14 36 39 00 00 36 00 D6 00 44 CD 08 FF 00 00 00 00 FF 00 00 00 00 FF 00 00 00 00 FF 00 00 00 00 7C 00 48 00 D6 00 E4 07 04 07 14 37 02 00 00 36 00 D6 00 44 CE 07 FF 00 00 00 00 FF 00 00

我知道文件中的每9个字节代表1个完整单位,其中包括6个变量: start 1个字节无符号整数, Devtype 1个字节有符号整数和4个字节 hexDevUID 和2字节无符号整数, day 均为1字节无符号整数。 这是我的代码,无法正常运行:

    #!/usr/bin/perl
    use feature qw(say);
    use strict;
    use warnings;
    
    
    use constant BUFSIZE => 9;
    my @input_file;
    
    @input_file = 'path\ZONE0.txt';
    
        open (my $BIN, "<:raw", $input_file[$i]) or die "can't open the file @input_file: $!";
        my $buffer;     
    
        while (1) {
            my $bytes_read = sysread $BIN, $buffer, BUFSIZE;
            die "Could not read file @input_file: $!" if !defined $bytes_read;
            last if $bytes_read <= 0;
            my @decimal= map { unpack "C", $_ } split //, $buffer;
            
            my $start= $decimal[0];
            
            my $DevType = $decimal[1];
            
            my @DevUID =@decimal[5,4,3,2];
            my $string_DevUID = join('', $string_DevUID);
            my $hexDevUID =sprintf("0x%x",$string_DevUID);
            
            
            
            my @year=@decimal[6,7];
            my $month=$decimal[8];
            my $day = $decimal[9];
            
            
            say $start;
            say $DevType;
            say $hexDevUID;
say @year;
        say $month;
        say $day;
        }
        

我的想法是一次读取9个字节,然后分别处理变量,但是我确信在 split和unpack 之间会丢失正确的值。我能够获取一些数据,但是值不正确,与我的十六进制编辑器显示的内容不匹配。我还尝试一次读取一个字节,并为大于1个字节的变量 joining 值,但是我仍然以某种方式弄乱了这些值,关于我做错了什么想法?

更新:我想split返回一个字符数组,并且在该步骤中格式可能会丢失,但是我不知道从$ buffer中获取数组的另一种方法。如果(如下所示),我的代码生成此错误,因为我尝试不拆包直接打开缓冲区。 :

Use of uninitialized value in join or string

2 个答案:

答案 0 :(得分:2)

一旦有了缓冲区,就不要将其视为字符串。 unpack立即使用正确的格式。不要把它当作char拆开,只是使其恢复为整数:

use v5.10;
my $buffer =
    "\x7C" . "\x00\x48\x00\xD6" . "\x00" .
    "\xE4\x07" . "\x04" . "\x07";

my @values = unpack 'CNCSCC', $buffer;

say "@values";

N可能是V。查看pack文档,看看您希望八位字节前进的方式。

然后,一旦有了数字,就可以对它们进行数字处理(无需任何顺序和移位。如果您适当unpack进行此操作,则无需执行此部分操作:

my @array = @decimal[5,4,3,2];
my $string_DevUID;
while( my( $i, $v ) = each @array ) {
    $string_DevUID += $v << ($i+1)
    }

答案 1 :(得分:-1)

因此,在将m头撞到墙上几次后,我得到了一个答案: 这是我修改的内容。我以其原始二进制格式读取了9个字节,所以我不会弄乱任何东西。我的缓冲区现在是字节数组,而不是小数。 后来我将数据相应地从二进制转换为十六进制或十进制。

while循环现在看起来像这样:(我添加了一个从二进制转换为十进制的子例程。)

while (1) {
            my $bytes_read = sysread $BIN, $buffer, BUFSIZE;
            die "Could not read file @input_file: $!" if !defined $bytes_read;
            last if $bytes_read <= 0;
            my @decimal= map { unpack "b8", $_ } split //, $buffer;
            
            my $stringstart= reverse split //, $decimal[0];
            my $start = bin2dec($stringstart);
            
            my $stringDevType= reverse split //, $decimal[1];
            my $DevType = bin2dec($stringDevType);
            
            my $stringDevUID = join('', $decimal[2], $decimal[3], $decimal[4], $decimal[5]);
            my $DevUID= reverse split //, $stringDevUID;
            my $HexDevUID = sprintf('%X', oct("0b$DevUID"));
            my $finalHexDevUID = '0x'.$HexDevUID;

ps:不需要反向连接,但是在我的上下文中,因为我的数据始终始终以最高有效位记录,因此我需要它。请随时评论任何可使我的代码看起来更优雅的优化。