Sunday, March 22, 2020

File I/O

Sometimes we want to read input from a file instead of having the user type it at the keyboard. Likewise, sometimes we want our output to be saved to a file instead of printed to the console. The way we do this is by reading from or writing to a file.

The open function

In order to read from or write to a file, the program must open that file. This is done by calling the open function, which uses the syntax open $var, $mode, $path. The first argument, $var, represents the variable that will be used to access the file moving forward. Often this is a newly declared variable, so make sure to prefix its name with the my keyword if so. The second argument represents the mode in which the file is to be opened and can take one of three values:

Value Meaning
< Read-only mode
> Write-only mode
>> Append mode

IMPORTANT NOTE: If a file that already exists is opened in write-only mode, the previous contents of that file will be overwritten (i.e. irretrievably gone). Append mode also allows information to be written to a file, but in append mode, the data written is tacked onto the end of whatever data was already in the file prior to its being opened.

The third argument contains the path to the file you want to open, which is relative by default unless you specify an absolute path (i.e. one that starts with a drive letter).

As long as you store the file to a local variable (i.e. one declared using the keyword my), Perl will automatically close the file once the variable goes out of scope. This makes it especially important that you double-check to make sure you have used the keyword my in declaring your file variable—Perl will treat the file variable as a global if you forget to do so, meaning that it will not be automatically closed. Suffice to say, that would not be good.

The usual practice when invoking the open function is to use the formulation open $var, $mode, $path or die $!. The or die $! part tells Perl to exit if the file cannot be opened for whatever reason. The special variable $! is used by Perl to store the error message generated by open if the file cannot be opened. For example, suppose the following program is saved as FileIO.plx. It attempts to open a text file called DNE.txt, which, as its name might suggest, does not exist:

open my $input, "<", "DNE.txt" or die $!;

When this program is run, it produces the output No such file or directory at FileIO.plx line 1. This tells us that Perl was unable to locate a file called DNE.txt in the current directory.

Reading from a File

Perl reads files one line at a time by enclosing the file variable in angle brackets < >. Note this is very similar to how we get user input from the keyboard using <STDIN>; STDIN is actually a special type of “file variable” that interfaces with the operating system to get input from the user’s keyboard. Suppose we have stored in the same directory as our program a text file called sample.txt with the following contents:

The quick brown fox jumped over the lazy dogs. She sells seashells by the seashore. Peter Piper picked a peck of pickled peppers.

The following program reads the file, one line at a time, and prints its contents to the console:

open my $input, "<", "sample.txt" or die $!; print $_ while (<$input>);

Writing to a File

Perl writes to files using a variation on the print statement: print $fileVariable $stuffToPrint. Note that there is no comma between $fileVariable and $stuffToPrint. This is important: if there is a comma, Perl will try to concatenate the two and print the result to the console. The following program will take our sample.txt from the previous example and copy its contents into a new file, copy.txt:

my @lines = (); { open my $input, "<", "sample.txt" or die $!; push @lines, $_ while (<$input>); } { open my $output, ">", "copy.txt" or die $!; print $output $_ for (@lines); }

Notice how I have put the read and write portions of the code in separate code blocks. It is good programming practice to only keep a file open for the minimum amount of time it absolutely has to be. As soon as I’m done using it, I let it the file variable go out of scope so that Perl will automatically close the file. Likewise, it is generally considered poor practice to have multiple files open at once unless this is absolutely necessary for some reason, hence why I use the @lines array as an intermediary rather than writing directly into copy.txt from sample.txt.

As you might have suspected based on the similarity between reading from files and reading from STDIN, printing to the console also uses a special type of “file variable,” called STDOUT. Writing to a file can also be accomplished by changing the default print location to another file variable using select $fileVariable;. If you choose to go this route, make sure to change the default print location back to the console when you’re done by calling select STDOUT;, as in the below example:

my @lines = (); { open my $input, "<", "sample.txt" or die $!; push @lines, $_ while (<$input>); } { open my $output, ">", "copy.txt" or die $!; select $output; print $_ for (@lines); select STDOUT; }

No comments:

Post a Comment