File Handling

16.1 Opening, Reading and Writing

use v5.36;
use autodie;                               # open/close/etc. die automatically on failure

# Three-argument open with lexical filehandles (always use this form)
open my $in,  '<',  'input.txt';           # read
open my $out, '>',  'output.txt';          # write (truncate)
open my $log, '>>', 'app.log';             # append
open my $rw,  '+<', 'data.bin';            # read/write

while (my $line = <$in>) {                 # line by line (memory-efficient)
    chomp $line;
    next unless length $line;
    print {$out} uc($line), "\n";          # braces around the filehandle are good style
}
say {$log} scalar(localtime), ' processed';
close $in; close $out; close $log;

# Without autodie: check every open
open(my $fh, '<', $file) or die "Cannot open '$file': $!";

# Slurp a whole file
my $content = do { local $/; open my $f, '<', 'input.txt'; <$f> };
my @lines   = do { open my $f, '<', 'input.txt'; <$f> };   # all lines (list context)
chomp @lines;

use Path::Tiny;                            # CPAN: the most convenient file API
my $text = path('input.txt')->slurp_utf8;
path('out.txt')->spew_utf8("Hello\n");
my @ls = path('input.txt')->lines_utf8({ chomp => 1 });
path('app.
…

16.2 Encodings and Binary Files

open my $u, '<:encoding(UTF-8)', 'unicode.txt';     # decode UTF-8 on read
open my $w, '>:encoding(UTF-8)', 'out.txt';
open my $bin, '<:raw', 'image.png';                  # binary
binmode $bin;
read($bin, my $buffer, 1024);                        # read 1024 bytes
seek($bin, 0, 0); tell($bin); eof($bin);

16.3 File Tests and Metadata

say 'exists'      if -e $file;
say 'plain file'  if -f $file;
say 'directory'   if -d $path;
say 'readable'    if -r $file;
say 'writable'    if -w $file;
say 'executable'  if -x $file;
say 'size: ', -s $file;           # size in bytes (false if zero)
say 'empty'       if -z $file;
say 'symlink'     if -l $file;
say 'age in days: ', -M $file;    # since last modification
my @st = stat($file);             # (dev, ino, mode, nlink, uid, gid, rdev, size, atime, mtime, ctime, blksize, blocks)
use File::stat; my $st = stat($file); say $st->size, ' ', scalar localtime $st->mtime;

rename 'old.txt', 'new.txt';
unlink 'temp.txt';                # delete
chmod 0644, 'file.txt';
utime undef, undef, 'file.txt';   # touch
use File::Copy qw(copy move);
copy('a.txt', 'b.txt');
move('b.txt', 'archive/b.txt');

16.4 In-Memory Files, Temp Files and Locking

open my $mem, '<', \$string;                # read from a string as if it were a file
open my $buf, '>', \my $captured;           # write into a string

use File::Temp qw(tempfile tempdir);
my ($tfh, $tname) = tempfile(UNLINK => 1);
my $tdir = tempdir(CLEANUP => 1);

use Fcntl qw(:flock SEEK_END);
open my $lockfh, '>>', 'shared.log';
flock($lockfh, LOCK_EX) or die "Can't lock: $!";   # exclusive lock
print {$lockfh} "safe write\n";
close $lockfh;                                     # releases the lock