```{index} single: open; modes, single: read mode, single: write mode, single: append mode ``` # Reading, writing, appending The three everyday tasks — read an existing file, create one from scratch, add to the end of one — are three different modes of the same [`open`](../../p5/core/perlfunc/open) call. This page shows each in the form that actually belongs in new code. ```{index} single: open; 3-arg form, single: open; 2-arg form, pair: open; security ``` ## The three-argument form ```perl open my $fh, MODE, EXPR or die "open: $!"; ``` - `my $fh` is a fresh lexical. `open` fills it in with a handle reference when it succeeds; on failure it leaves `$fh` undefined and sets `$!`. - `MODE` is a short string — `"<"`, `">"`, `">>"`, and a handful of others — that says what you want to do. - `EXPR` is the pathname. It is treated as a plain string: leading or trailing whitespace is preserved, and the string is never reinterpreted as a shell command. The older two-argument form (`open $fh, "<$path"`) still works, but it re-parses the string looking for mode characters and for the "magic" pipe syntax. Do not feed it a filename that came from a user. ```{index} single: readline, single: diamond operator, single: chomp, single: close, single: $/ ``` ## Reading ```perl my $path = "/etc/hostname"; open my $fh, "<", $path or die "open $path: $!"; while (my $line = <$fh>) { chomp $line; # ... work with $line ... } close $fh or die "close $path: $!"; ``` Two observations that trip up beginners: - `<$fh>` calls [`readline`](../../p5/core/perlfunc/readline) under the hood. In boolean context — as inside the `while` head — the result is tested for being defined, not truthy. That matters because a file containing the line `"0\n"` would otherwise stop the loop early. `while (<$fh>)` is a special-case spelling that Perl rewrites to `while (defined($_ = <$fh>))` for you. - `close` can fail. On a read handle that is rare, but on a write handle it is the moment buffered data is finally flushed, so a closing error is a real error. Check it. ```{index} single: slurp mode, single: $/; slurp, single: local ``` If you want the whole file as one string, slurp it: ```perl open my $fh, "<", $path or die "open $path: $!"; local $/; # undef = slurp mode my $all = <$fh>; close $fh or die "close $path: $!"; ``` The `local $/` is the idiomatic "for the rest of this scope, turn off line splitting". When control leaves the enclosing block, `$/` returns to its previous value. ```{index} single: print; to filehandle, single: truncate; on open, single: buffered I/O ``` ## Writing (clobber) ```perl open my $fh, ">", $path or die "open $path: $!"; print $fh "first line\n"; print $fh "second line\n"; close $fh or die "close $path: $!"; ``` - `">"` **creates** the file if it does not exist, and **truncates** it to zero bytes if it does. There is no confirmation step. - The filehandle does not need a comma after it in a `print` invocation. `print $fh "text"` is correct; `print $fh, "text"` is a different expression that treats `$fh` as a list element. - Buffered writes are held in memory until `close`, an explicit flush, or the buffer fills. If your program crashes between `print` and `close`, a writer with pending buffered data loses it. Always pair a writing `open` with a checked `close`. ```{index} single: append mode, single: PIPE_BUF, single: atomic append ``` ## Appending ```perl open my $fh, ">>", $path or die "open $path: $!"; print $fh scalar(localtime), " event\n"; close $fh or die "close $path: $!"; ``` - `">>"` creates the file if needed and positions the write cursor at the end. - On a POSIX kernel, each `write(2)` on an append-mode descriptor is atomic with respect to the file's end-of-file marker. Two processes appending short lines to the same log therefore interleave line-by-line, not byte-by-byte. Do not rely on that for lines longer than `PIPE_BUF` bytes. ```{index} single: close; error checking, single: flush ``` ## Closing as a real step, not a formality A failed `close` on a write handle typically means the kernel refused to flush — out of space, I/O error, network filesystem trouble. Treat it exactly like a failed `print`: ```perl close $fh or die "close $path: $!"; ``` Ignoring it is how "the file looked fine during testing" becomes "production wrote half a log entry and moved on." ```{index} single: read-write mode, single: sysopen, single: sysread, single: syswrite, single: atomic rename ``` ## What about read-write? `"+<"` opens a file for both reading and writing, without truncating. It is a real mode and it works, but it is also the source of most "my file came out corrupted" bug reports. If you need random-access reads with in-place overwrites, reach for [`sysopen`](../../p5/core/perlfunc/sysopen), [`sysread`](../../p5/core/perlfunc/sysread), and [`syswrite`](../../p5/core/perlfunc/syswrite) so the buffering layer does not guess at seek positions on your behalf. For almost every job labelled "edit the file", the right answer is write a new file, fsync, rename over the old one. That is the pattern atomic editors use. ## Next With reading, writing, and appending covered, the next question is what the bytes *mean*. See [Encoding and layers](encoding-and-layers).