Free Files, Testing Files, and Binary Data
Files and file systems don't contain just the data that you put into files. On the one hand, filehandles sometimes represent more than a simple file. They may stand for such things as the keyboard, the monitor screen, network sockets, or mass storage devices such as tape drives.
Also, in addition to the data stored in the files, a file system contains what's called metadatathat is, data about the data in its files. Perl can be used to get metadata out of the file system; for example, to determine how big your file is, when it was last changed, who changed it, and information about what's in the file. With some operating systems, the file metadata can even determine whether the file is treated as a text file or a binary file.
Free Filehandles
Perl began life as a Unix utility, and sometimes pieces of its heritage show througheven on non-Unix platforms. When your Perl program is started, it receives three filehandles that are "free"that is, they are opened automatically without you having to do any programming work to open them. They are STDOUT (standard output), STDIN (standard input), and STDERR (standard error). By default, they are connected to your terminal.
As you type, Perl can read your input from the STDIN filehandle:
$guess=<STDIN>;
When you want to display output, you use print. The reason why your output has appeared on the screen is that, by default, print uses the STDOUT filehandle:
print "Hello, World!\n"; # is the same as... print STDOUT "Hello, World!\n";
In Hour 12, you'll learn how to change print's default filehandle.
STDERR is usually set to your terminal, like STDOUT, but it is used for displaying error messages. In Unix, error messages and normal output can be sent to different display devices, and writing error messages to the STDERR filehandle is traditional. The die and warn functions both write their messages to STDERR. If your operating system does not have a separate error-reporting filehandlefor example, Windows or MS-DOSyour STDERR output will go to the STDOUT device.
NOTE
Redirecting error and output messages in Unix is well beyond the scope of this book, and the technique varies depending on what shell you use. Any good book on using Unix should cover this topic thoroughly.
Text Files and Binary Files
Some operating systemssuch as VMS, Atari ST, and notably Windows and MS-DOSmake the distinction between binary files (raw) and text files. This distinction causes problems because Perl can't really tell the differenceand you wouldn't want it to.
Text files are simply records that end in end-of-line characters, called record separators. Binary files, on the other hand, are collections of bits that need to be treated literally, such as images, executable programs, and data files.
When you're writing a text file, Perl translates the \n character sequence into the record separator that your operating system uses. In Unix, \n becomes an ASCII 10 (LF); on a Macintosh, ASCII 13 (CR); and on DOS and Windows systems, it becomes the sequence ASCII 13 and ASCII 10 (CRLF). When you're writing text, this behavior is appropriate.
When you're writing binary dataGIF files, EXE files, MS Word documents, and so ontranslation isn't what you want. Any time you really need to write binary data, and don't want Perl or the operating system to translate it for you, you must use the binmode function to mark the filehandle as binary. Use binmode after the filehandle is opened but before you do any input or output from it:
open(FH, ">camel.gif") || die "$!"; binmode(FH); # The filehandle is now binary. # Start of a valid GIF file... print FH "GIF87a\056\001\045\015\000"; close(FH);
You have to use binmode on the filehandle only once, unless you close it and reopen it. Using binmode on systems that do not distinguish between binary files and text files (Unix, Macintosh) causes no harm.
File Test Operators
Before you open a file, sometimes it's nice to know whether the file exists, whether the file is really a directory, or whether opening the file will give a permission denied error. If you could examine the file's metadata, you could get answers to these questions. For these situations, Perl provides the file test operators. The file test operators all have the following syntax:
-X filehandle -X pathname
Here, X is the particular test you want performed, and filehandle is the filehandle you want tested. You can also test a pathname without having an open filehandle. Table 5.2 lists some of the operators.
Table 5.2 Short List of File Test Operators
Operator |
Example |
Result |
-r |
-r 'file' |
Returns true if 'file' is readable |
-w |
-w $a |
Returns true if the filename contained in $a is writeable |
-e |
-e 'myfile' |
Returns true if 'myfile' exists |
-z |
-z 'data' |
Returns true if 'data' exists but is empty |
-s |
-s 'data' |
Returns size of 'data' in bytes if it exists |
-f |
-f 'novel.txt' |
-Returns true if 'novel.txt' is a regular file rather than a directory |
-d |
-d '/tmp' |
Returns true if '/tmp' is a directory |
-T |
-T 'unknown' |
Returns true if 'unknown' appears to be a text file |
-B |
-B 'unknown' |
Returns true if 'unknown' appears to be a binary file |
-M |
-M 'foo' |
-Returns the age (in days) since the file 'foo' was modified since this program began |
You can view the full list of file test operators in the online documentation. Type perldoc perlfunc at a command prompt, and look in the section "Alphabetical List of Perl Functions."
The following snippet uses file test operators to verify that files don't already exist before they are overwritten and to determine the age of the file since it was last modified:
print "Save data to what file?"; $filename=<STDIN>; chomp $filename; if (-s $filename ) { warn "$file contents will be overwritten!\n"; warn "$file was last updated ", -M $filename, "days ago.\n"; }