Correctly opening a file in Perl
Let's start with some code, shall we?
use strict;
use warnings;
use v5.14;
# Let's just pull off the first argument and use it
# as our filename
my $filename = $ARGV[0];
if ($filename) {
# Discussed more in detail below
open(my $fh, '<', $filename) or die "$!";
# Close the file, even though it will close when the
# filehandle falls out of scope
close($fh);
}
Three-argument open
The first important takeaway here is using three argument open.
There exists a version of open that takes only two arguments, and the mode (<, >, |-, or -|) is concatenated with the filename.
This is bad, for obvious reasons. Let's say we had code that looked like this:
open(my $fh, $filename);
What would happen if we passed in a file that named '>myfile'? In that case, Perl would instead open that file with for writing rather than reading, as we intended.
Checking the result of open()
It's very important to check the result of open()
, as files will refuse to open for all sorts of reasons (they don't exist, permissions, etc.). It's important to report this back to the user, and take the appropriate action. In my case, since this program doesn't do anything other than open a file, I die
.
Lexical filehandles
Now you might have seen code that uses a glob instead of a scalar to hold a filehandle.
open(FH, '>', $filename);
This is bad, as the filehandle isn't automatically closed when FH falls out of scope. While you ought to manually close your filehandles when you're done with them, making them scalars causes them to close automatically when they fall out of scope, providing an extra safeguard.
Not only that, but they don't pollute as many namespaces as typeglobs do, however that's a subject for another time.
Written by William Orr
Related protips
2 Responses
Thanks for the feedback.
I made sure to only call open
if $filename isn't a falsey value, and to explain my reasoning behind die
in the protip
Remember people also might be using unicode characters.
open(my $fh, "<:encoding(UTF-8)", "filename") or die $!;