PrevIndexNext

Reading and Writing Files

One of Perl's mottos is:

In some languages even easy things are hard.
In some languages, hard things are not possible.

Text files are ubiquitous - they are the 'lingua franca' of the cyber world. Configuration files, HTML, Postscript, RTF, program source files, XML, email. All of these are text files. Reading and writing them should be easy.

Reading Files

With your favorite text editor create file of names:
names.txt ------- Art Bob Zoe Christian Sabrina Marge
Here is a Perl program to read that file:
open NAMEIN, "names.txt"; $line = <NAMEIN>; print "hello, $line"; close NAMEIN;
It reads the first line and prints:
hello, Art
The name "NAMEIN" is called a file handle. It is conventionally in UPPER CASE.

To read all lines:

open NAMEIN, "names.txt"; while ($line = <NAMEIN>) { print "hello, $line"; } close NAMEIN;
Note that we are making an assignment to $line within the boolean of the while. This is okay. The value of an assignment is the value that was assigned.

The loop will terminate when the end of the file is reached. At this point $line will be assigned 'undef' which is false.

If the file "names.txt" may not exist you need to watch for that:

if (not open NAMEIN, "names.txt") { print "cannot open names.txt\n"; exit; }
The function 'exit' will immediately exit the program. Nothing else will happen. The function 'die' prints an error message and then exits. So equivalently we have:
if (not open NAMEIN, "names.txt") { die "cannot open names.txt\n"; }

The usual, more concise, and fun way of writing this is:

open NAMEIN, "names.txt" or die "cannot open names.txt\n";
If you are curious WHY it could not open the file you can do this:
die "cannot open names.txt: $!\n";
$! contains the error message from the last system command that failed. The mnemonic for this is "What Went Bang?". :)

$! is a rather cryptic variable name but it is one of many that are commonly used in Perl so you might as well start getting used to them! Other common ones are $_, @_, $/, $|, $&. They all have some clever mnemonic.

Writing Files

To CREATE a text file:
open OUT, ">nums.txt" or die "cannot create new.txt: $!\n"; for ($i = 1; $i <= 10; ++$i) { print OUT "$i\n"; } close OUT;
Note the ">" as the first character of the second parameter to open. This causes the file to be created (it is truncated if already exists) and opened for writing.

Note that the same built-in function 'print' is used to print to the standard output (the terminal screen) and to print to files. This is nice but may be confusing. The difference is that when printing to a file you put a filehandle directly after 'print'. It is important that you do NOT put a COMMA (,) after the filehandle in the print statement. See above. There is no comma after the 'OUT'.

When opening a file for output the file will be overwritten and truncated if it already exists. If you want to add to the end of the file rather than starting all over again you can do this:

open OUT, ">>data.txt" or die "could not append to data.txt: $!\n";
This opens the file for appending. See the ">>" instead of ">".

A Better Way

These notes were written in 2001. There is now (2012) a better and more robust way to read and write files. It is explained fully in the book "Perl Best Practices". A simple example should suffice:

# # copy a file replacing all a's with A. # open my $in, '<', "data.txt" or die "cannot open data.txt: $!\n"; open my $out, '>', "newdata.txt" or die "cannot create newdata.txt: $!\n"; while (my $line = <$in>) { $line =~ s{a}{A}g; print {$out} $line; } close $in; close $out;

Exercises

  1. Using your text editor put an assortment of numbers, one per line, in a text file. Open this file up, read the lines and send the even numbers to one file (your choice of name) and the odd ones to another. Hint: You can use the modulo operator '%' to determine if a number is even or not.
    $x % 2
    gives the remainder when $x is divided by 2. This will be 0 for even numbers and 1 for odd numbers.
  2. Ask the user for a filename. Append the suffix ".txt" to the filename and try to open the file for reading. If you can't ask the user again. Keep asking until you get a good filename. Assume the file has numbers in it one per line. Add up all of the numbers and print the sum into a new file with a name the same as the input file but with the word "SUM" on the front. For example:
    nums.txt -------- 3 123 9 35 SUMnums.txt ----------- 170
    The user would give the filename as "nums".
  3. Open a file named "files.txt" and read the lines. Each line of this file contains another filename. Append ".txt" to these lines and open that name. Read all the lines counting them.

    Print a report of how many lines each file had and a grand total at the end. If you couldn't open a file say so. It doesn't matter what is on the lines. Just count them.

    For example:

    files.txt --------- hello great fun hello.txt --------- bonjour guten tag buenos dias great.txt --------- fabulous wonderful
    The report would look like this:
    hello: 3 great: 2 fun: not found total: 5
  4. To get the current date and time you can do this:
    $t = localtime; print "$t\n";
    This will print a line like:
    Wed Jun 4 00:34:06 2003
    Make a perl script that asks the user for their name and age and accumulates this information in a log file. The log file will have the current date/time (as above), the name and the age - all separated by tabs. With several runnings of your script the log file might look like this:
    Wed Jun 4 10:34:06 2003 Joseph 34 Wed Jun 4 10:35:10 2003 Mary 12 Wed Jun 4 10:36:01 2003 Linda 3
    Modify the script to continue to ask for names and ages until you give 'q' as a name.

PrevIndexNext