Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW

How to sort record using pack function ?

by bh_perl (Monk)
on Sep 02, 2010 at 02:52 UTC ( #858459=perlquestion: print w/replies, xml ) Need Help??

bh_perl has asked for the wisdom of the Perl Monks concerning the following question:


This is sample of my binary file
84 47 00 0c 00 00 11 0a 03 50 35 04 00 64 0a
The record length located at second byte with length 2 bytes. Based on the sample, the record length is "47 00" and after it convert to decimal it must be 71.

That mean before convert the hex value to decimal, i must sort the number from "47 00" to "00 47". After that, the number can be convert to decimal which is "0047" to "71".

My question, is it possible to sort the record using pack function ?. Could you give some example...

Replies are listed 'Best First'.
Re: How to sort record using pack function ?
by AnomalousMonk (Archbishop) on Sep 02, 2010 at 03:04 UTC

    See pack, unpack and perlpacktut:

    >perl -wMstrict -le "my $raw = pack 'H*', '8447000c0000110a0350350400640a'; my ($n) = unpack 'x v', $raw; print $n; " 71

    Update: If  $n is the count of a number of items that follow it, see the '/' unpack template specifier:

    >perl -wMstrict -le "my $raw = pack 'H*', '8407000c0b0a09080706'; my @chars = unpack 'x v/C', $raw; print qq{@chars}; " 12 11 10 9 8 7 6

      This is some of my coding..could you comment ?. I read the record length as $length = unpack "C4", substr $data,0,2,'';. is it correct ?
      read (DATA, $data, 3); $tag = unpack "H2", substr $data,0,1,''; if ($tag =~ /(81|80|00)/) { # the Record Identifier H80 is used as Filler record to fill in o +ne byte if ($tag == "80") { $length = 2; } # the Record Identifier H81 is used as Filler record to fill in s +everal bytes if ($tag == "81") { $length = unpack "C4", substr $data,0,2,''; $length -= 3; } # the Record Identifier H00 is used as Filler record to fill in 3 +2 bytes if ($tag == "00") { $length=62; } read (DATA, $data, $length); $raw = unpack "H*", $data; if ($trace) { print ("FILLER\n"); printf ("%-20s : %-5s\n", "TAG", $tag); printf ("%-20s : %-5s\n", "RECORD LENGTH", $length); print ("RAW DATA : \n$raw\n"); } }

        It is vital for me and any other monk who may wish to comment on your code to know the structure of the data you are trying to parse with the code. Without a clear definition of this structure, we're just guessing. One important point to make clear is if you are trying to parse raw binary data, or if the data is in some form such as, literally, '1A2B334566778B9A' as is suggested by your use of the 'H' unpack specifier.

        I am having trouble understanding exactly what kind of data that you really have. This matters. Do you have real binary? Or are you using some textual string representation of binary data?

        AnomalousMonk immediately converted your string into "real binary", which is not unreasonable as you can't post a binary file here, you'd have to uuencode it or whatever. It could be that you are working from a spec and don't have a real binary file to work from. Below shows to write one to disk for testing.

        #we are packing nibbles (4 bits) at a time # H is big-endian order # h is little-endian order (swaps adjacent nibbles) my $raw = pack 'H*', '8447000c0000110a0350'; #decode back to input string... print unpack ('H*', $raw), "\n"; # prints: 8447000c0000110a0350 print unpack ('h*', $raw), "\n"; # prints: 487400c0000011a03005 #need to set binmode when writing raw binary data... open (BIN, '>', 'test.bin') || die "can't open test.bin"; binmode(BIN); print BIN $raw; close BIN;
        Yes, there are a number of ways to write the code that gets the length:
        # v1 is a little-endian 16 bit (short) so needs 2 bytes # if substr didn't limit to 2 bytes substr($raw,1) works also # unpack will use first 2 bytes. my ($len) = unpack 'v1',substr($raw,1,2);#substr deals in bytes not ni +bbles. print $len,"\n"; #prints 71 print unpack ('v', substr($raw,1)),"\n"; #prints 71 print unpack ('x v', $raw ),"\n"; #prints 71
        so questions:
        1. Do you really have real binary or are you just working from this "nibblelist" format in ASCII?
        2. It would be helpful if you had a little chart explaining or the C record declaration or equivalent in whatever format you can:
        record: unsigned char record_type 8 bits unsigned short length 16 bits little endian ... some data type and description of these sections...
        After see they single byte of type and the 2 bytes (little endian) of length, then there are 3 different types of data sections that can follow depending upon the type, please explain that a bit more - not sure that I really get it.

        I would suggest reading more about substr. I think you probably want to leave off REPLACEMENT parm. The $str = substr eXPR, OFFSET, LENGTH, REPLACEMENT

        If you really want to read binary, read more about read as you will have to set binmode and also pay attention to the return value of read which is the actual number of bytes read. All of this is different if you just have an ASCII representation of binary instead of "real binary".

      Hi..Thank you very much for your help..

      Could you shared how the program knows the record length ?
        Could you shared how the program knows the record length ?

        I'm not sure I understand this question. You have defined the 'record length' to be two bytes (16 bits) in little-endian (or 'VAX') order in the second and third positions in your example string. The 'v' unpack template specifier unpacks just such a value. (The 'x' skips over the first character/byte position.)

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://858459]
Approved by mr_mischief
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (8)
As of 2023-02-02 13:55 GMT
Find Nodes?
    Voting Booth?
    I prefer not to run the latest version of Perl because:

    Results (19 votes). Check out past polls.