2

I am new to perl. I am trying to write code that reads data from a binary file, and returns it in a specifique format. it can be either decimal, unsigned, hexadecimal... my binary file when opened in a hex editor looks something like this:

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

I know that every 9 bytes in the file represent 1 full unit witch includes 6 variables: start a 1 byte unsigned integer, Devtype 1 byte signed integer, and 4 bytes hexDevUID , year a 2 bytes unsigned integer , month and day each is 1 byte unsigned integer. here is my code which doesnt function correctly:

    #!/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;
        }
        

my idea was to read the 9 bytes at a time and then treat the variables separately, but I am sure I am losing the correct values in between the split and unpack. I am able to get some data but the values are not correct they dont match to what my hex editor shows. I have also tried reading one byte at a time and joining values for the variables with more than 1 byte but I still somehow mess up the values, Any ideas on what I am doing wrong?

update: I guess split returns an array of characters and Formats might be lost in that step but I dont know of an other way to get an array out of $buffer. My code generates this error if (as suggested below) I try to unpack the buffer directly without the split. :

Use of uninitialized value in join or string
0

2 Answers 2

2

Once you have the buffer, don't treat is as a string. unpack it right away with the right formats. Don't take it apart as chars just to make it back into ints:

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

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

say "@values";

That N might be a V. Look at the pack docs to see which way you want the octets to go.

Then, once you have numbers, do numeric things with them (without whatever order and shifting makes sense. You don't need to do this part if you unpack it appropriately though:

my @array = @decimal[5,4,3,2];
my $string_DevUID;
while( my( $i, $v ) = each @array ) {
    $string_DevUID += $v << ($i+1)
    }
Sign up to request clarification or add additional context in comments.

2 Comments

i am sorry i dont get it. what N and what v? and what does 'CNSCC' do? i cant find much in unpack documentation
Actually, the unpack points pack. I also have extensive examples in Programming Perl.
-1

so after banging m head against the wall a few times i got to an answer: here is what I modified. I read the 9 Bytes in their original binary Format so that I dont mess with anything. my Buffer now is an Array of Bytes and not decimals. I later converted data accordingly from binary to either hexadecimal or decimal.

the while loop is now Looking like this: (I added a subroutine that converts from binary to decimal.)

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: The reverse fonction is not necessary but in my context I have there because my data is Always recorded most significant bit first and so I needed it. please feel free to comment any optimizations that can make my Code look classier.

1 Comment

This is really not a good way to do this. If you want an octet, use C. Then you don't have to do anything to have it as an octet. Also, sprintf('0x%X', ...) gets you the 0x before the hex representation, but that's also what the format %#x does for you already!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.