File Read Write Create with IO::File

Ran into an annoying gotchya with Perl’s IO::File. Apparently opening the file in append mode with read access if the file already exists puts the file position pointer at the end of the file. If it doesn’t exist, it creates the file. Note the +>>, that opens the file r/w/append. You can also use the more common (and more easily recognizable) form of a+.

    my $FH = new IO::File "$file", "+>>";
    while (my $line = $FH->getline()) {
      print "Line: $line\n";
    }
    undef $FH;

I noticed that when I tried to read the file (if it already existed), then nothing would be read. I neglected to realize that you must seek to position 0 in the file if you want to read it. Therefore the following code will work:

    my $FH = new IO::File "$file", "+>>";
    $FH->seek(0,0);
    while (my $line = $FH->getline()) {
      print "Line: $line\n";
    }
    undef $FH;

Although it might seem obvious that you need to be at the beginning of the file to read it forward (and it is), I didn’t realize the file pointer opened a file in append mode to the last position in the file (in hind sight, it does appear to be a bit more obvious).

Posted in Perl. Tags: . Comments

Testing For A Number

Although you generally don’t have to worry about types in Perl, it is occasionally necessary to ensure that you are working with numbers. Your test cases should notify you that something is amiss when you didn’t get a number (when you were expecting one). Thankfully Scalar::Util provides a method to deal with this.

use Scalar::Util qw( looks_like_number );

my @possibleNumbers = qw(1 5.25 word 4);

foreach my $nums (@possibleNumbers) {
       print "$nums is", looks_like_number($nums) ? '' : ' not', " a number\n";
}

This will print:

1 is a number
5.25 is a number
word is not a number
4 is a number

This neat little method takes advantage of Perl C API’s looks_like_number() function. Since this is virtually native, it will be pretty fast.

Posted in Perl. Tags: . Comments

Cleaning Up Long Conditionals With Grep

Every so often I am faced with testing a few conditionals before dropping into another control structure. If you have to test out a few conditionals, then its likely a dispatch table won’t be useful. If you have a lot of conditionals to test, you’ll likely not want to deal with an ugly expression like the following:

if ( (defined $SITE{$partner}{'foo'} && ($SITE{$partner}{'foo'} > 0) ) and
        ( (defined $assoc{'foo'} && ($assoc{'foo'} > 0)) or
          (defined $assoc{'bar'} && ($assoc{'bar'} > 0)))
   ) {
print "We're here!\n";
}

One of the ways to deal with it to to use grep. Since grep returns the number of elements in the array that evaluate to true (when called in a scalar context), I can do the following to make it work:

my @foo = [ $SITE{$partner}{'foo'}, $assoc{'foo'}, $assoc{'bar'} ];
if ( (grep {defined($_) and $_ > 0} @foo) > 2) { print "We're here!\n"; }

The reason this works is that when called in the scalar context, grep only returns the number of elements that evaluate to true. Since there are 3 elements in the array, the return value is greater than 2, then the entire expression evaluates to true. This is not only a lot easier to read and a lot cleaner to write, but it makes for easy additions to the conditional testing if necessary. Although changing the context in which functions are called is common, it is easily forgotten. Grep called in scalar context can be easily manipulated (as above) to add readability to your program.

Posted in Perl. Tags: , . Comments

Mac Perl Problems After Feb Update

When I did my most recent upgrade (the latest Mac software updates), it broke my Perl install. In order to figure out if your Perl is broken like mine was, you will get a result like this:

beacon:mail elubow$ perl -MIO
IO object version 1.22 does not match bootstrap parameter 1.23 at /System/Library/Perl/5.8.8/darwin-thread-multi-2level/XSLoader.pm line 94.
Compilation failed in require.
BEGIN failed--compilation aborted.

I had a little trouble finding out how to fix this. So I am posting this here in case it helps someone else out. It was a simple fix (since CPAN doesn’t work) that you have to do by hand. Go to the CPAN site and download dist IO here. Download and untar it and run the following commands:

beacon:IO-1.2301 elubow$ sudo perl Makefile.PL
Writing Makefile for IO
beacon:IO-1.2301 elubow$ sudo make install
cc -c   -arch i386 -arch ppc -g -pipe -fno-common -DPERL_DARWIN -no-cpp-precomp -fno-strict-aliasing -Wdeclaration-after-statement -I/usr/local/include -O3   -DVERSION=\"1.23\" -DXS_VERSION=\"1.23\"  "-I/System/Library/Perl/5.8.8/darwin-thread-multi-2level/CORE"   IO.c
...
<strong>Removed for brevity</strong>
...
Files found in blib/arch: installing files in blib/lib into architecture dependent library tree
Installing /System/Library/Perl/5.8.8/darwin-thread-multi-2level/auto/IO/IO.bundle
Writing /System/Library/Perl/5.8.8/darwin-thread-multi-2level/auto/IO/.packlist
Appending installation info to /System/Library/Perl/5.8.8/darwin-thread-multi-2level/perllocal.pod

This should fix your Perl install. It also ended up that I had to run CPAN and reinstall Scalar::Util and Storable.