Unless you're using artificial intelligence to model a solipsistic philosopher,
your program needs some way to communicate with the outside world. In
lines 3 and 4 of our grade example you'll see the word GRADES
, which
exemplifies another of Perl's data types, the filehandle. A filehandle is
just a name you give to a file, device, socket, or pipe to help you
remember which one you're talking about, and to hide some of the
complexities of buffering and such. (Internally, filehandles are
similar to streams from a language like C++, or I/O channels from
BASIC.)
Filehandles make it easier for you to get input from and send output to many different places. Part of what makes Perl a good glue language is that it can talk to many files and processes at once. Having nice symbolic names for various external objects is just part of being a good glue language.[17]
[17] Some of the other things that make Perl a good glue language are: it's 8-bit clean, it's embeddable, and you can embed other things in it via extension modules. It's concise, and networks easily. It's environmentally conscious, so to speak. You can invoke it in many different ways (as we saw earlier). But most of all, the language itself is not so rigidly structured that you can't get it to "flow" around your problem. It comes back to that TMTOWTDI thing again.
You create a filehandle and attach it to a file by using the open
function. open takes two parameters: the filehandle and the filename
you want to associate it with. Perl also gives you some predefined (and
preopened) filehandles. STDIN
is your program's normal input
channel, while STDOUT
is your program's normal output channel. And
STDERR
is an additional output channel so that your program can make
snide remarks off to the side while it transforms (or attempts to
transform) your input into your output.[18]
[18] These filehandles are typically attached to your terminal, so you can type to your program and see its output, but they may also be attached to files (and such). Perl can give you these predefined handles because your operating system already provides them, one way or another. Under UNIX, processes inherit standard input, output, and error from their parent process, typically a shell. One of the duties of a shell is to set up these I/O streams so that the child process doesn't need to worry about them.
Since you can use the open function to create filehandles for various purposes (input, output, piping), you need to be able to specify which behavior you want. As you would do on the UNIX command line, you simply add characters to the filename.
open(SESAME, "filename"); # read from existing file open(SESAME, "<filename"); # (same thing, explicitly) open(SESAME, ">filename"); # create file and write to it open(SESAME, ">>filename"); # append to existing file open(SESAME, "| output-pipe-command"); # set up an output filter open(SESAME, "input-pipe-command |"); # set up an input filter
As you can see, the name you pick is arbitrary.
Once opened, the filehandle SESAME
can be used to access the file or pipe
until it is explicitly closed (with, you guessed it, close(SESAME)
),
or the filehandle is attached to another file by a subsequent open
on the same filehandle.[19]
[19] Opening an already opened filehandle implicitly closes the first file, making it inaccessible to the filehandle, and opens a different file. You must be careful that this is what you really want to do. Sometimes it happens accidentally, like when you say
open($handle,$file)
, and$handle
happens to contain the null string. Be sure to set$handle
to something unique, or you'll just open a new file on the null filehandle.
Once you've opened a filehandle for input (or if you want to use
STDIN
), you can read a line using the line reading operator,
<>
. This is also known as the angle operator, because of its
shape. The angle operator encloses the filehandle (<SESAME>
)
you want to read lines from.[20]
An example using the STDIN
filehandle to read an answer supplied
by the user would look something like this:
[20] The empty angle operator,
<>
, will read lines from all the files specified on the command line, orSTDIN
, if none were specified. (This is standard behavior for many UNIX filter programs.)
print STDOUT "Enter a number: "; # ask for a number $number = <STDIN>; # input the number print STDOUT "The number is $number\n"; # print the number
Did you see what we just slipped by you? What's the STDOUT
doing in
those print statements there? Well, that's one of the ways you can use
an output filehandle. A filehandle may be supplied as the first
argument to the print statement, and if present, tells the output
where to go. In this case, the filehandle is redundant, because the
output would have gone to STDOUT
anyway. Much as STDIN
is the
default for input, STDOUT
is the default for output. (In line 18 of
our grade example, we left it out, to avoid confusing you up till now.)
We also did something else to trick you. If you try the above
example, you may notice that you get an extra blank line. This
happens because the read does not automatically remove the newline
from your input line (your input would be, for example,
"9\n
"). For those times when you do want to remove
the newline, Perl provides the chop and
chomp functions. chop will indiscriminately remove (and return)
the last character passed to it, while chomp will only remove the end of record marker
(generally, "\n
"), and return the number of
characters so removed. You'll often see this idiom for inputting a
single line:
chop($number = <STDIN>); # input number and remove newline
which means the same thing as
$number = <STDIN>; # input number chop($number); # remove newline