LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 2818 min read

Working with Text Files

Learn how to read a file into an array, process it line by line, and write results out to a new file.

Introduction

File processing is one of the reasons Perl became so popular for real-world automation. This lesson walks through the modern, safe way to open files, read them either all at once or line by line, and write processed results back out to disk.

What You Will Learn
  • How to open files safely with the three-argument form of open.
  • How to slurp a whole file into an array.
  • How to process a file line by line with a while loop.
  • How to write filtered or transformed results to a new file.

Opening Files Safely

Modern Perl file handling uses lexical filehandles (a scalar like $fh, not a bareword like FH) and the three-argument form of open, which separates the filename from the mode. This avoids ambiguity and accidental code injection if a filename contains special characters.

data.txt
apple,10
banana,3
cherry,7

The examples below assume a file called data.txt in the current directory with the contents shown above.

Reading an Entire File into an Array

Reading a filehandle in list context (assigning it to an array) reads every line into a separate array element, each one still ending in its newline character. This is convenient for small-to-medium files where you want random access to specific lines.

use strict;
use warnings;
open(my $fh, '<', 'data.txt') or die "Cannot open data.txt: $!\n";
my @lines = <$fh>;
close $fh;
print "Total lines: ", scalar(@lines), "\n";
print $lines[0];
Output

Click Run to see what this code prints.

Reading Line by Line

For larger files, reading one line at a time inside a while loop is far more memory-efficient, since only one line is held in memory at any point. This is the pattern you will use most often for log files and data exports.

use strict;
use warnings;
open(my $fh, '<', 'data.txt') or die "Cannot open data.txt: $!\n";
while (my $line = <$fh>) {
chomp $line;
my ($item, $qty) = split /,/, $line;
print "$item -> $qty units\n";
}
close $fh;
Output

Click Run to see what this code prints.

Trimming Newlines with chomp

Every line read from a filehandle keeps its trailing newline character. chomp removes exactly one trailing newline (if present) from the end of a string, modifying it in place. Always chomp a line before comparing or splitting it, or the newline will silently end up attached to the last field.

Writing to a New File

Opening a file with mode '>' creates it if it does not exist and truncates it if it does. Combined with reading, you can build simple filter-and-transform scripts that read from one file and write results to another.

use strict;
use warnings;
open(my $in, '<', 'data.txt') or die "Cannot open data.txt: $!\n";
open(my $out, '>', 'report.txt') or die "Cannot open report.txt: $!\n";
while (my $line = <$in>) {
chomp $line;
my ($item, $qty) = split /,/, $line;
print $out "$item: $qty\n" if $qty > 5;
}
close $in;
close $out;
print "Report written.\n";
Output

Click Run to see what this code prints.

Appending vs Overwriting

Mode '>' overwrites a file from scratch; mode '>>' appends to the end of an existing file (creating it if needed) without touching what is already there - the mode you want for logs that should accumulate over time.

use strict;
use warnings;
open(my $log, '>>', 'activity.log') or die "Cannot open activity.log: $!\n";
print $log "Script ran successfully.\n";
close $log;

Common Mistakes

Avoid These Mistakes
  • Forgetting chomp, leaving a trailing newline attached to the last field after split.
  • Using the old two-argument open, which is ambiguous and unsafe with untrusted filenames.
  • Not checking the return value of open with or die - a failed open silently returns undef otherwise.
  • Reading a huge file entirely into an array when line-by-line processing would use far less memory.
  • Overwriting a source file by opening it for both writing and reading with the same mode by mistake.

Best Practices

  • Always use three-argument open with a lexical filehandle: open(my $fh, '<', $filename).
  • Always check the result of open and die with a helpful message including $!.
  • chomp every line you read before processing it.
  • Prefer line-by-line reading for large files; slurp into an array only when you need random access.
  • Close filehandles explicitly when you are done writing, so buffered output is flushed promptly.

Frequently Asked Questions

Lexical filehandles are closed automatically when they go out of scope, but explicit close catches write errors immediately and makes intent clear.

It holds the operating system's error message for the most recent failed system call, such as "No such file or directory".

Add an encoding layer to open: open(my $fh, '<:encoding(UTF-8)', $filename).

Key Takeaways

  • Use three-argument open with a lexical filehandle and check for failure with die.
  • Reading a filehandle in list context slurps every line into an array.
  • A while loop reading one line at a time is the memory-efficient default.
  • chomp removes the trailing newline before you process a line.
  • Mode '>' overwrites a file; mode '>>' appends to it.

Summary

File I/O is where Perl really earns its reputation for practical scripting - reading, transforming, and writing text is the backbone of countless real-world automation tasks. Next, you will see how to squeeze similar power into a single command-line one-liner.

Next Lesson →

Perl One-Liners