The only thing I used that could be accused of being slightly obscure is:
do { local $/ = <$infile> }
This is a fairly commonly used idiom for reading an entire file into a single string, but exactly how it works is somewhat obscure.
Firstly, when you call <$filehandle>, Perl reads a single line from the file $filehandle. See perldoc -f readline
Secondly, the variable $/ is used by Perl's file reading function to indicate what character to use as a line terminator (technically it's called the record separator). So normally, $/ is set to "\n". If you set $/ to undef, then Perl won't treat any characters as line terminators, so the readline function will simply read the entire remainder of the file. See perldoc -f readline and perldoc perlvar
So, just based on the above knowledge, we can slurp the entire contents of a filehandle into a string like this:
$/ = undef;
my $string = <$filehandle>;
But actually, what if other parts of our code rely on $/ being set to "\n"? We don't want to permanently undefine it.
my $old_terminator = $/;
$/ = undef;
my $string = <$filehandle>;
$/ = $old_terminator;
Because temporarily changing the value of a variable is such a common need, Perl provides a shortcut. The local keyword allows you to set a new temporary value for a variable for a particular code block, such that the variable's old value will be automatically restored at the end of the block. See perldoc -f local. So our code becomes:
my $string;
{
local $/ = undef;
$string = <$filehandle>;
}
But = undef is redundant because all newly declared scalar variables (including local ones) are undefined. So now we have:
my $string;
{
local $/;
$string = <$filehandle>;
}
Now, the do block allows Perl to run a block of code and return the result of the last statement. See perlsyn. So our code can become:
my $string = do {
local $/;
<$filehandle>;
}
The last simplification relies on the fact that in the following statement:
local $/ = <$filehandle>
Perl does things in this order:
- Localizes $/, setting it to undef.
- Reads the file - the entire file because $/ is undef.
- Performs the assignment.
Thus we end up with the situation where you can read the entire contents of an open file handle into a string using:
my $string = do { local $/ = <$infile> };
Now, of course I could have included the entire explanation above as a comment, but I try to stick to a policy of never writing comments which are longer than the code itself. |