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

Sure, I could do something like (big endian):
read FILE, $myinput, 3; ($byte1, $byte2, $byte3) = unpack('C3', $myinput); $myinteger = (65536 * $byte1) + (256 *$byte2) + $byte3;
But that doesn't seem very elegant.

Replies are listed 'Best First'.
Re: How do I read a 24 bit integer?
by JavaFan (Canon) on Jan 01, 2010 at 14:44 UTC
    Untested:
    my $myinput = "\x00"; read FILE, $myinput, 3, 1; my $myinteger = unpack 'N', $myinput;
    Basically, you're making a 32-bit integer by prefixing the 24-bit integer with 8 0-bits. Given a 32-bit integer, you can unpack it.
      I think I understand that, but a couple of curves:

      1. How would I handle little endian data? Would it work correctly with no offset?

      2. The real data file (of course) is somewhat more complicated. It has several consecutive 24 bit integers. So how would I read and unpack say four at a time?

        1. Little endian would have 8 trailing 0 bits, wouldn't it? I'm sure you'll be able to add a NUL byte to a string.
        2. How about reading them into four variables, and prepending them with NUL bytes?
Re: How do I read a 24 bit integer?
by AnomalousMonk (Archbishop) on Jan 01, 2010 at 21:02 UTC

    Not sure if this is more elegant than the OPed solution or than JavaFan's, but...

    >perl -wMstrict -le "my $be24s = qq{\x01\x02\x03\x04\x05\x06\x07\x08\x09}; printf qq{%08lx }, $_ for unpack_be24($be24s); sub unpack_be24 { my ($s) = @_; return map unpack('N', qq{\x00$_}), unpack '(a3)*', $s; } " 00010203 00040506 00070809

    Again, to make this little-endian, append the null byte and use  'V' for the unpack template.

    Note: Of course, this doesn't actually read a 24-bit integer as requested in the OP, but I figure the reading part should be fairly straightforward.

    Update: A slightly different approach, with one less map and an extra pack:

    >perl -wMstrict -le "my $be24s = qq{\x11\x12\x13\x24\x25\x26\x37\x38\x39}; my $le24s = reverse $be24s; printf qq{%08lx }, $_ for unpack_be24($be24s); print ''; printf qq{%08lx }, $_ for unpack_le24($le24s); sub unpack_be24 { my ($s) = @_; return unpack 'N*', pack '(xa3)*', unpack '(a3)*', $s; } sub unpack_le24 { my ($s) = @_; return unpack 'V*', pack '(a3x)*', unpack '(a3)*', $s; } " 00111213 00242526 00373839 00373839 00242526 00111213
Re: How do I read a 24 bit integer?
by Anonymous Monk on Jan 02, 2010 at 12:19 UTC
    Sorry. I have to repeat this here. It seems I can't go down enough levels to read even my own last reply. As I said, this code:
    #!/usr/bin/perl -w use strict; my $myinput="\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06"; my($var1, $var2, $var3, $var4) = unpack('A3A3A3A3', $myinput); $var1 = unpack ('N', "\x00" . $var1); $var2 = unpack ('N', "\x00" . $var2); $var3 = unpack ('N', "\x00" . $var3); $var4 = unpack ('N', "\x00" . $var4); print "$var1\n"; print "$var2\n"; print "$var3\n"; print "$var4\n";
    produces:
    Use of uninitialized value in concatenation (.) or string at ./test2.p +l line 10. Use of uninitialized value in concatenation (.) or string at ./test2.p +l line 11. 66051 263430
    When I would expect to see:
    0 0 66051 263430

      Click on a note's title in order to show the responses beneath it.

      Pack mojo is always interesting and very deep! I found this in the pack manpage in order to explain your problem and BrowserUK's solution:

      When unpacking, "A" strips trailing whitespace and nulls, "Z" strips everything after the first null, and "a" returns data verbatim.