File I/O & Text Processing
📄 File I/O & Text Processing
📂 Opening and Closing a File
This is the modern, 3-argument form of open — filehandle, mode, filename, each as separate arguments. The modes: '<' for reading, '>' for writing (truncates the file first — Course 4-equivalent caution applies), '>>' for appending. Always use the 3-argument form; the older 2-argument form (open($fh, "< $filename")) mixes the mode and filename into one string, which is a genuine security and correctness hazard if $filename ever contains unexpected characters.
Chapter 5's open(...) or die "..."; idiom isn't optional politeness — without it, a script that fails to open a file (wrong path, missing permissions) continues running anyway, silently doing nothing useful with an unopened filehandle. This is a genuinely common source of "my script ran with no errors but produced no output" confusion. Every open call should be followed by or die (or equivalent error handling — Course 2's Error Handling chapter covers alternatives).
🔁 Reading Line by Line — the while (<FH>) Idiom
<$fh> reads one line at a time from the filehandle — this is the single most iconic Perl idiom, appearing in essentially every script that ever touches a file. Each line returned includes its trailing newline character; chomp removes exactly that one trailing newline (and only if present), which is why it's almost always the first thing done to a freshly-read line.
📥 Reading an Entire File at Once
$/ is the special variable controlling where a line "ends" — normally a newline. Temporarily undefining it (local $/;, using Chapter 2's local to restore it automatically afterward) makes <$fh> read the whole file in one call instead of one line at a time — the classic "slurp" idiom. Course 2's CPAN chapter covers Path::Tiny, a module that wraps this pattern into a single tidy method call.
✍️ Writing to a File
print $fh "text"; is correct — print $fh, "text"; is a real, easy-to-make mistake. With the comma, Perl treats $fh as just another item in the list being printed to standard output, printing the filehandle's internal stringification followed by "text" — not writing to the file at all. This asymmetric-looking syntax (a filehandle directly followed by the thing to print, no separator) is one of Perl's odder corners, but it's exactly how print is told which filehandle to target.
🔧 A Worked Example — Processing a Log File
This is a genuine, small end-to-end script combining nearly everything covered so far: a subroutine (Chapter 7) that reads a file line by line, uses regex capture groups (Chapter 6) to classify each line, tallies results in a hash (Chapter 4), and returns it context-appropriately — followed by writing a sorted summary report to a new file. This is exactly the shape of real, everyday Perl text-processing work.
File Handling: Perl vs. Python
Python's with open(...) as f: context manager guarantees the file closes automatically when the block ends. Perl's lexical filehandles (my $fh) have a similar safety net built in, less obviously: since $fh is just an ordinary lexical scalar, the file it holds open closes automatically once $fh itself goes out of scope — no special syntax required. That said, an explicit close($fh); remains standard practice in real code, since it makes the exact moment a resource is released clear to a reader, rather than relying on scope boundaries alone.
3-argument open
open($fh, '<', $filename) or die ...; — always check the result.
while (<$fh>)
Reads one line at a time; chomp strips the trailing newline.
Slurp mode
local $/; reads an entire file into one scalar in one call.
print $fh "..."
No comma between the filehandle and what's being printed.
💻 Coding Challenges
Challenge 1: Write Then Read
Write a script that opens a file for writing, writes 3 lines to it using print $fh, closes it, then reopens the same file for reading and prints each line (with chomp applied) using the while (<$fh>) idiom.
Challenge 2: Count Lines Matching a Pattern
Given a text file with several lines, write a subroutine count_matching(filename, pattern) that returns how many lines match a given regex pattern, using the while (<$fh>) idiom and =~.
Challenge 3: Fix the Missing or die
Given open(my $fh, '<', 'does_not_exist.txt'); with no error handling, explain in one sentence what happens when this line runs against a genuinely missing file, then rewrite it correctly using this chapter's or die idiom.
🎯 What's Next
Next chapter: References — scalar/array/hash references, anonymous data structures, and arrow notation.