Some Tricks With Filehandles in Perl

Discussion in 'Perl' started by pradeep, May 28, 2008.

  1. pradeep

    pradeep Team Leader

    Joined:
    Apr 4, 2005
    Messages:
    1,645
    Likes Received:
    87
    Trophy Points:
    0
    Occupation:
    Programmer
    Location:
    Kolkata, India
    Home Page:
    http://blog.pradeep.net.in

    Read An Entire File All At Once



    At times we need to read the contents of a file all at once, instead of reading line by line, or reading the contents line-wise into an array, here's how we can read the contents of a file into a scalar as text.

    Code:
      open FILE, '</tmp/page.html' or die $!;
      flock FILE, 1 or die $!; # wait for lock
      seek(FILE, 0, 0); # move pointer to beginning
      my $slurp = do{local $/; <FILE>}; # slurp ;-)
      close(FILE); # close file, automatically releases the lock
      
      print $slurp;
      
    If you're sure you really want to fill your computer's operating memory with the contents of a file, then this trick will accomplish that. The angle brackets "<>" work on a file handle by returning either: the next record, in a scalar context or a list of all the records, in an array context. It would be wise to format your data into manageable records, separated by some token characters. The special Perl variable $/ can be set to that record separator. By default $/ will be a newline, but by undefining it you cause perl to consider the entire file to be one single record. Because $/ is a global variable changing it in one place will have side-effects elsewhere. For that reason we create a limited scope and localize our $/ redefinition inside it by wrapping the work in a do{...} block. Note also that we lock the file for reading, which will wait for any other flocks to release before we try to access it.

    Doing The Same In A Better Manner



    The above example looks ugly, so we install the IO::All module and things become much cleaner. The IO::All module brings together several powerful IO modules and offers a single unified interface to all of them.

    Code:
      use IO::All;
      
      my $file = io("./anthem.txt")->lock;
      my $slurp = $file->slurp;
      $file->close;
      
      print $slurp;

    Assign One Filehandle To Another



    Code:
      open(MYOUT, "> log.log");
      *STDOUT = *MYOUT;
      
      print "message";
    (text file "log.log" now contains string "message")

    You probably have used perl's print function with a filehandle before, but did you know that even if you don't use a filehandle perl assumes you mean a default one called "STDOUT"? C programmers will recognize that as the standard output -- usually the screen, or terminal window (or the browser, when writing CGIs). What we've done here is create our own filehandle, pointing to a file. Then we've done something a little sneaky -- we've used the star symbol-prefix to refer to STDOUT as a typeglob. Typeglobs allow use to create an "alias" of sorts, making all variables of a certain name point to another variable of a certain name. The second line of this snippet basically says that the STDOUT variable now aliases to the MYOUT variable. Once this is done any prints to the default filehandle go instead to our own filehandle.

    Writing To Two Filehandles At The Same Time



    If, for whatever reason you wanted to print the same string to two places at once you are trying to do what the Unix utility "tee" does. This functionality is available in your perl script even if you aren't running it on Unix, via the IO::Tee module. Tee is object-oriented so you must first create a Tee object using the new constructor. This sub takes two arguments, each can be either a string representing a filehandle or a reference to an open filehandle. In this case we use a string representing an appending filehandle, pointing to a file called "debuglog.txt" and a built-in filehandle called STDOUT. This built-in filehandle is automatically created and it is in fact the default target that print points to (usually the terminal or, for a CGI the browser). To get a reference to a filehandle requires that we use the back-slash operator on a typeglob symbol, the star. Typeglobs are a special way to refer to all variables of a given name at once (regardless of its "type", array, hash, scalar etc.). Its necessary to use the star because filehandles have no prefix symbol of their own. new returns an instance object of the class Tee, and we assign this to the scalar $tee. Now whenever we print to the $tee object we are actually sending the string to two places at once! Cool, isn't it?

    Code:
      use IO::Tee;
      
      $tee = IO::Tee->new(">> debug.log", \*STDOUT);
      
      print $tee "an error occurred on ".scalar(localtime)."\n";

    File Contents
    Code:
      an error occurred on Fri May 27 07:44:20 2008
     
  2. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83
  3. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83
  4. Karapaz

    Karapaz Banned

    Joined:
    May 18, 2009
    Messages:
    7
    Likes Received:
    1
    Trophy Points:
    0
    Occupation:
    USA
    Location:
    USA
    Ive got Cygwin Perl 5.10.0 experimental installed, not the XAMPP perl add-on.My CGI was failing because it couldnt find any of the perl libs. The apache error log said things like:Code:
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice