The inverse operation - packing byte contents from a string of hexadecimal
digits - is just as easily written. For instance:
- my $s = pack( 'H2' x 10, map { "3$_" } ( 0..9 ) );
+ my $s = pack( 'H2' x 10, 30..39 );
print "$s\n";
Since we feed a list of ten 2-digit hexadecimal strings to C<pack>, the
pack template should contain ten pack codes. If this is run on a computer
with ASCII character coding, it will print C<0123456789>.
-
=head1 Packing Text
Let's suppose you've got to read in a data file like this:
Date |Description | Income|Expenditure
- 01/24/2001 Ahmed's Camel Emporium 1147.99
+ 01/24/2001 Zed's Camel Emporium 1147.99
01/28/2001 Flea spray 24.99
01/29/2001 Camel rides to tourists 235.00
Hence, putting it all together:
- my($date,$description,$income,$expend) = unpack("A10xA27xA7xA*", $_);
+ my ($date, $description, $income, $expend) =
+ unpack("A10xA27xA7xA*", $_);
Now, that's our data parsed. I suppose what we might want to do now is
total up our income and expenditure, and add another line to the end of
how much we've spent:
while (<>) {
- my($date, $desc, $income, $expend) = unpack("A10xA27xA7xA*", $_);
+ my ($date, $desc, $income, $expend) =
+ unpack("A10xA27xA7xA*", $_);
$tot_income += $income;
$tot_expend += $expend;
}
# OK, let's go:
- print pack("A10xA27xA7xA*", $date, "Totals", $tot_income, $tot_expend);
+ print pack("A10xA27xA7xA*", $date, "Totals",
+ $tot_income, $tot_expend);
Oh, hmm. That didn't quite work. Let's see what happened:
- 01/24/2001 Ahmed's Camel Emporium 1147.99
+ 01/24/2001 Zed's Camel Emporium 1147.99
01/28/2001 Flea spray 24.99
01/29/2001 Camel rides to tourists 1235.00
03/23/2001Totals 1235.001172.98
format pads any non-existent characters with spaces, so we can use the
additional spaces to line up our fields, like this:
- print pack("A11 A28 A8 A*", $date, "Totals", $tot_income, $tot_expend);
+ print pack("A11 A28 A8 A*", $date, "Totals",
+ $tot_income, $tot_expend);
(Note that you can put spaces in the template to make it more readable,
but they don't translate to spaces in the output.) Here's what we got
this time:
- 01/24/2001 Ahmed's Camel Emporium 1147.99
+ 01/24/2001 Zed's Camel Emporium 1147.99
01/28/2001 Flea spray 24.99
01/29/2001 Camel rides to tourists 1235.00
03/23/2001 Totals 1235.00 1172.98
$tot_income = sprintf("%.2f", $tot_income);
$tot_expend = sprintf("%12.2f", $tot_expend);
$date = POSIX::strftime("%m/%d/%Y", localtime);
- print pack("A11 A28 A8 A*", $date, "Totals", $tot_income, $tot_expend);
+ print pack("A11 A28 A8 A*", $date, "Totals",
+ $tot_income, $tot_expend);
This time we get the right answer:
and pass C<$buf> to your send routine. Some protocols demand that the
count should include the length of the count itself: then just add 4
-to the data length. (But make sure to read L<"Lengths and Widths"> before
+to the data length. (But make sure to read L</"Lengths and Widths"> before
you really code this!)
You will probably find these modifiers even more useful if you have
to deal with big- or little-endian C structures. Be sure to read
-L<"Packing and Unpacking C Structures"> for more on that.
+L</"Packing and Unpacking C Structures"> for more on that.
=head2 Floating point Numbers
pack codes C<f>, C<d>, C<F> and C<D>. C<f> and C<d> pack into (or unpack
from) single-precision or double-precision representation as it is provided
by your system. If your systems supports it, C<D> can be used to pack and
-unpack extended-precision floating point values (C<long double>), which
-can offer even more resolution than C<f> or C<d>. C<F> packs an C<NV>,
-which is the floating point type used by Perl internally. (There
-is no such thing as a network representation for reals, so if you want
-to send your real numbers across computer boundaries, you'd better stick
-to ASCII representation, unless you're absolutely sure what's on the other
-end of the line. For the even more adventuresome, you can use the byte-order
-modifiers from the previous section also on floating point codes.)
+unpack (C<long double>) values, which can offer even more resolution
+than C<f> or C<d>. B<Note that there are different long double formats.>
+
+C<F> packs an C<NV>, which is the floating point type used by Perl
+internally.
+
+There is no such thing as a network representation for reals, so if
+you want to send your real numbers across computer boundaries, you'd
+better stick to text representation, possibly using the hexadecimal
+float format (avoiding the decimal conversion loss), unless you're
+absolutely sure what's on the other end of the line. For the even more
+adventuresome, you can use the byte-order modifiers from the previous
+section also on floating point codes.
=head2 Uuencoding
-Another odd-man-out in the template alphabet is C<u>, which packs an
+Another odd-man-out in the template alphabet is C<u>, which packs a
"uuencoded string". ("uu" is short for Unix-to-Unix.) Chances are that
you won't ever need this encoding technique which was invented to overcome
the shortcomings of old-fashioned transmission mediums that do not support
work on older Perls you'll need to C<unpack( 'Z* Z* C')> to get the length,
then use it to make a new unpack string. For example
- # pack a message: ASCIIZ, ASCIIZ, length, string, byte (5.005 compatible)
+ # pack a message: ASCIIZ, ASCIIZ, length, string, byte
+ # (5.005 compatible)
my $msg = pack( 'Z* Z* C A* C', $src, $dst, length $sm, $sm, $prio );
# unpack
After reading L<perlfunc> explaining how to use C<syscall> we can write
this Perl function copying a file to standard output:
- require 'syscall.ph';
+ require 'syscall.ph'; # run h2ph to generate this file
sub cat($){
my $path = shift();
my $size = -s $path;