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

I am trying to converting a C program to Perl. One of the main stopping points with me is the fact that I have a C structure that is something like this:
struct data{
char id2;
int secs;
int version;
char misc10;
char data9999;
};
How do you convert this to Perl?  I'm pretty sure it has something to do with the pack() command, but I'm not exactly sure how.  The problems I am having are:

a. let's say secs=65.  I need it to output 0x0041 or \#0065 as opposed to print out "65" ascii.  In other words I need it to be binary and fill up 2 bytes.

b. how do you load and establish a 10,000 byte array, or create a 10,000 byte array filled with binary 0's as opposed to ascii 0's to write input and output. 
 I know about the chr(), ord() commands, but is there an easy way to just establish or read this type of structure?  

thanks in advance.
-Greg

Replies are listed 'Best First'.
Re: Converting a C structure to Perl
by Fastolfe (Vicar) on Jan 05, 2001 at 12:10 UTC
    Remember to close your <code> tags and your question won't be so difficult for us to read. :)

    If you're looking to find the Perl equivalent of that data structure, another poster already nailed it for you: use a hash. If you're looking to pull in binary data created from that C structure into your Perl program, you probably want unpack. Something like this might do what you're looking for:

    $_ = ...; # binary data in $_ my ($id, $secs, $version, $misc, $data) = unpack("a2iia10a9999", $_);
    The format string (described in pack) basically means: 2 bytes of data, two integers, 10 bytes of raw data followed by 9999 bytes of raw data. Other things you might be able to take advantage of: Z for null-terminated strings, c for individual characters/bytes and n/N for "network" short/longs.

    If you need to generate binary data with information you have, simply change unpack to pack and reverse your arguments around. Pass $id/$secs/etc and get back a binary representation. The format string remains the same.

    When you say you want a 10,000 byte array, what are you talking about? Are you talking about a C array? I don't know how you expect to judge how many bytes a Perl array will consume. If you want 10,000 elements, that's already been given to you. 10,000 bytes in a string could be done by: "\0" x 10_000 Perhaps you could elaborate and/or use some more appropriate terminology so we can figure out what you mean? Heh.

    Hope this helps.

Re: Converting a C structure to Perl
by repson (Chaplain) on Jan 05, 2001 at 12:02 UTC
    True you could use the pack command but perl programmers usually use an anonymous hash.
    my $item = { char => 'df', secs => 65, version => 4, misc => 'data', data => 'misc' }; # access with $item->{char} = 'ef'; # the print you asked for, outputs 0x0041 printf ("0x%04x", $item->{secs}); # what do you mean binary 0's? something like this? my @array = ("\000") x 10000;
    For more information you might like to look at perldata and perlref from perldoc.
Re: Converting a C structure to Perl
by Caillte (Friar) on Jan 05, 2001 at 15:15 UTC

    You are making a common c-programmer mistake, one that I regularly made when I started programming in perl. From what I see of your note you equate scalars with strings. This is not the case. Consider the following:

    my $a; $a = "Mary had a little lamb"; # $a contains a string $a = 2001; # $a now contains a number $a = '2001'; # $a STILL contains a number $a .= 'AD'; # $a is a string once more :)

    How this prints is another matter. print, printf etc all output strings. This is their nature, they know naught else ;). The simplest way to output the byte value of a number is to use the following:

    print pack("i", $a);

    This will work the same way whiether you represent your data as a number or as a strng of numeric characters; perl interprets both as a number.

    print pack("i", 3000); ¨Y¡á print pack("i", '3000'); ¨Y¡á print pack("i", "3000"); ¨Y¡á

    Perhaps something like the simple object below will help you

    my $data = Data->new("aa", 65, 1.2, "misc", "data string"); print $data->print_secs(); package Data; sub new { my ($class, $id, $secs, $vers, $misc, $data) = @_; my $self = { 'id' => $id, 'secs' => $secs, 'version' => $vers, 'misc' => $misc, 'data' => $data, }; bless $self, $class; return $self; } sub print_secs { # Returns $self->{'secs'} as a byte stream $self = shift; return pack('i', $self->{'secs'}); }

    This is a bit crude but perhaps it will give you some ideas.

      I don't tend to use it, but I figure mentioning Class::Struct might be interesting to you guys:
      use Class::Struct; struct data => { id => '$', secs => '$', version => '$', misc => '$', data => '$', next => 'data', # just an example }; $one = new data (id => 1, secs => 2); $two = new data (id => 2, secs => 2, data => 'hi'); $one->next($two); print $one->next->data; # "hi"