File handles

[ Perl tips index ]
[ Subscribe to Perl tips ]

Whenever we need to deal with reading and writing to files in Perl, we use a file handle. In order to get a file handle for a file, we need to open that file using open. Traditionally file handles are presented as bare words in all upper-case:

        # open file for reading
        open(IN, "<", $filename) or die $!;

        # open file for writing
        open(OUT, ">", $filename) or die $!;

        # add line numbers to each line
        my $line_no = 1;

        while(<IN>) {
                print OUT "$line_no: $_";

Bare word file handles exist on a package basis, starting from where the file is opened until the file is closed or the program exits. Attempting to open another file to a file handle of the same name will close the existing file handle before re-opening it to the new file. When this occurs in a subroutine you call while processing your file contents you can end up with a bug which is hard to track down. This is one example of ``action from a distance''.

Fortunately, in Perl 5.6.0 and above, we can also use scalar file handles when opening files. These file handles can be given a lexical scope by using the my keyword, just like any other Perl scalar.

        # open file for reading
        open(my $input_fh, "<", $filename) or die $!;

        # open file for writing
        open(my $output_fh, ">", $filename) or die $!;

        # copy file
        my $line_no = 1;
        while(<$input_fh>) {
                print $output_fh "$line_no: $_";

Scalar file handles are easily passed to subroutines or stored in data structures. Further, when they go out of scope the file is closed. This prevents many of the ``action from a distance'' problems that may occur with the traditional file handles, and can also help reduce the number of files needlessly left open.

Printing to file handles

When printing to a file there is no comma in between the file handle and the arguments to be printed. However adding the comma is an easy mistake to make, and often a very hard bug to spot:

        # This does not print to the OUT file handle
        print OUT, $line, $sep, $_;

        # This does not print to the $out file handle
        print $out, $line, $sep, $_;

The lack of a comma between the file handle and the list of things to print, allows Perl to recognise the file handle as something special. This is called an indirect argument and is similar to the optional code block in sort, grep and map. Unfortunately the comma is so small that it can be hard for us to use to recognise that we've added it. Worse still, another programmer (or ourselves in six months time) may inadvertently insert the comma at a later date, believing the original statement to be incorrect or ``strange''.

A good solution is for us to make the file handle more prominent. Capitalised bare words provide this, but have drawbacks as mentioned above. An excellent alternative, examined by Dr Damian Conway in his ``Perl Best Practices'' book, is to add curly braces around the file handle.

        # Note we require a '*' before a bare word file handle.
        print {*OUT} $line, $sep, $_;

        # This makes it clear that {$out} is something special.
        print {$out} $line, $sep, $_;

Just like sort, grep and map we now know that if these curly braces are followed by a comma, we've made a mistake. The braces also make it very clear, even to a novice programmer, that {$out} is something special, and not just another variable to be printed.

Adopting a naming convention where all file handles end in _fh can also make it more obvious when they are being misused. $config_fh is obviously a configuration file handle, whereas $config may not be.

[ Perl tips index ]
[ Subscribe to Perl tips ]

This Perl tip and associated text is copyright Perl Training Australia. You may freely distribute this text so long as it is distributed in full with this Copyright noticed attached.

If you have any questions please don't hesitate to contact us:

Phone: 03 9354 6001 (Australia)
International: +61 3 9354 6001

Valid XHTML 1.0 Valid CSS