tags:

views:

145

answers:

1

A common 'Perlism' is generating a list as something to loop over in this form:

for($str=~/./g) { print "the next character from \"$str\"=$_\n"; }

In this case the global match regex returns a list that is one character in turn from the string $str, and assigns that value to $_

Instead of a regex, split can be used in the same way or 'a'..'z', map, etc.

I am investigating unpack to generate a field by field interpretation of a string. I have always found unpack to be less straightforward to the way my brain works, and I have never really dug that deeply into it.

As a simple case, I want to generate a list that is one character in each element from a string using unpack (yes -- I know I can do it with split(//,$str) and /./g but I really want to see if unpack can be used this way...)

Obviously, I can use a field list for unpack that is unpack("A1" x length($str), $str) but is there some other way that kinda looks like globbing? ie, can I call unpack(some_format,$str) either in list context or in a loop such that unpack will return the next group of character in the format group until $str is exausted?

I have read The Perl 5.12 Pack pod and the Perl 5.12 pack tutorial and the Perkmonks tutorial

Here is the sample code:

#!/usr/bin/perl
use warnings;
use strict;

my $str=join('',('a'..'z', 'A'..'Z')); #the alphabet...  

$str=~s/(.{1,3})/$1 /g;                #...in groups of three
print "str=$str\n\n";

for ($str=~/./g) { 
 print "regex: = $_\n";
}

for(split(//,$str)) {
 print "split: \$_=$_\n";
}

for(unpack("A1" x length($str), $str)) {
 print "unpack: \$_=$_\n";
}
+6  A: 

pack and unpack templates can use parentheses to group things much like regexps can. The group can be followed by a repeat count. * as a repeat count means "repeat until you run out of things to pack/unpack".

for(unpack("(A1)*", $str)) {
    print "unpack: \$_=$_\n";
}

You'd have to run a benchmark to find out which of these is the fastest.

cjm
I knew it had to be simple!!! Now that I play with it, `"(A1)*"` will glob (my question) and `"(A1)$i"` will generate $i fields. Peachy! Do you know where that is documented in a good way? Most of the material on the Web was not great...
drewk
@drewk: in `perldoc -f pack` (http://perldoc.perl.org/functions/pack.html) at "Supplying a * for the repeat count instead of a number means to use however many items are left..." and "A ()-group is a sub-TEMPLATE enclosed in parentheses..."
Ether
@Ether: the examples in perldoc did not speak to me in this case. Most of the uses of `*` were in the form of 'gobble up the rest' as in `unpack 'a3/A A*', '007 Bond J '; gives (' Bond', 'J')` and I think the perldoc on pack/unpack could be -- more clear...
drewk
@Ether: The example under the ()group template of `pack("@1A((@2A)@3A)", qw[X Y Z]) produces "\0X\0\0YZ"` is an example only the author could love `:-}}`
drewk
It's definitely not the clearest documentation!
Ether