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

Posted

技术标签:

【中文标题】读取二进制数据并将其转换为十六进制【英文标题】:reading a binary data and converting it to hex 【发布时间】:2020-09-23 23:47:58 【问题描述】:

我是 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字节 hexDevUIDyear 2 个字节的无符号整数,monthday 每个都是 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 个字节,然后分别处理变量,但我确信我在 拆分和解包之间丢失了正确的值。我能够获得一些数据,但值不正确,它们与我的十六进制编辑器显示的不匹配。我还尝试一次读取一个字节并加入超过 1 个字节的变量的值,但我仍然以某种方式弄乱了这些值,关于我做错了什么有什么想法吗?

更新:我猜 split 返回一个字符数组,格式可能会在该步骤中丢失,但我不知道有其他方法可以从 $buffer 中获取数组。如果(如下所示)我尝试在不拆分的情况下直接解压缩缓冲区,我的代码会生成此错误。 :

Use of uninitialized value in join or string

【问题讨论】:

【参考方案1】:

一旦你有了缓冲区,不要把 is 当作一个字符串。 unpack 立即使用正确的格式。不要把它作为字符拆开,只是为了让它回到整数:

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)
    

【讨论】:

很抱歉,我不明白。什么 N 和什么 v? “CNSCC”是做什么的?我在解包文档中找不到太多内容 其实unpack指向pack。我在Programming Perl 中也有大量示例。【参考方案2】:

所以在我用头撞墙几次之后,我得到了一个答案: 这是我修改的。我阅读了原始二进制格式的 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:反向函数不是必需的,但在我的上下文中我有,因为我的数据总是首先记录最高有效位,所以我需要它。请随时评论任何可以使我的代码看起来更优雅的优化。

【讨论】:

这确实不是一个好方法。如果您想要一个八位字节,请使用 C。那么您无需执行任何操作即可将其作为八位字节。此外,sprintf('0x%X', ...) 会在十六进制表示之前为您提供0x,但这也是%#x 格式已经为您所做的!

以上是关于读取二进制数据并将其转换为十六进制的主要内容,如果未能解决你的问题,请参考以下文章

C ++读取二进制文件并转换为十六进制

使用十六进制数据读取文件并将其存储到python中的列表中

如何将 Python 十进制转换为 SQLite 数字?

将十六进制字符串转换为图像文件[重复]

Perl - 将十六进制转换为二进制并将其用作字符串

在 Python 中将 PNG 转换为二进制(base 2)字符串