


                                                       Chapter 11
                                                FILE INPUT/OUTPUT

FILES HANDLE SERIAL DATA
_________________________________________________________________

One of the most common operations when using a computer is to
either read from, or write to a file.  You are already somewhat
experienced in file handling from the last chapter, because in
computer terminology, the keyboard, terminal, and printer are all
classified as files.  A file is any serial input or output device
that the computer has access to.  Since it is serial, only one
piece of information is available to the computer at any instant
of time. This is in contrast to an array, for example, in which all
elements of the array are stored internally and are all available
at any time.


A SHORT HISTORY LESSON
_________________________________________________________________

Several years ago computers were all large cumbersome machines with
large peripheral devices such as magnetic tape drives, punch card
readers, paper tape readers or punches, etc.  It was a simple task
to assign the paper tape reader a symbol and use that symbol
whenever it was necessary to read a paper tape.  There was never
more than one file on the paper tape being read, so it was simply
read sequentially, and hopefully the data was the desired data. 
With the advent of floppy disks, and hard disks, it became
practical to put several files of data on one disk, none of which
necessarily had anything to do with any of the other files on that
disk.  This led to the problem of reading the proper file from the
disk, not just reading the disk.

Pascal was originally released in 1971, before the introduction of
the compact floppy disk.  The original release of Pascal had no
provision for selecting a certain file from among the many included
on the disk.  Each compiler writer had to overcome this deficiency
and he did so by defining an extension to the standard Pascal
system.  Unfortunately, all of the extensions were not the same,
and there are now several ways to accomplish this operation.  There
are primarily two ways, one using the Assign statement, and the
other using the Open statement.  They are similar to each other and
they accomplish the same end result.


BACK TO THE PRESENT TIME
_________________________________________________________________

All of the above was described to let you know that we will have
a problem in this chapter, namely, how do we cover all of the
possible implementations of Pascal available?  The answer is, we
can't.  Most of what is covered in this chapter will apply to all

                                                        Page 11-1

                                   Chapter 11 - File Input/Output

compilers, and all that is covered will apply to the TURBO Pascal
compilers.  As we have mentioned before, this tutorial is
especially written for the TURBO Pascal compilers, but you should
be warned that you will find differences in Pascal implementations
if you have occasion to use a different Pascal compiler someday. 
You may, for example, need to use a mini-computer or a mainframe
computer someday to complete a programming assignment.  When that
happens, you will find that the area of input/output control will
probably be the biggest difference in the implementations of
Pascal.


READING AND DISPLAYING A FILE
_________________________________________________________________

Examine the file READFILE.PAS for an example of  ================
a program that can read a text file from the       READFILE.PAS
disk.  In fact, it will read itself from the     ================
disk and display itself on the video monitor. 
The first statement in the program is the Assign
statement.  This is TURBO Pascal's way of selecting which file on
the disk will be either read from or written to.  In this case we
will read from the disk.  The first argument in the Assign
statement is the device specifier similar to Lst used in the last
chapter for the printer.  We have chosen to use the name Turkey for
the device identifier, but could have used any valid identifier. 
This identifier must be defined in a var declaration as a text type
variable as illustrated in line 4.  The next argument is the
filename desired.  The filename can be defined as a string
constant, as it is here, or as a string variable.

The text type is a predefined type and is used to define a file
identifier.  It is predefined as a "file of char", so it can only
be used for a text file.  We will see later that there is another
type of file, a binary file.

You will find that the operating system that you are using requires
a file name to follow certain conventions when it is named, and the
Pascal programming language has a set of rules by which an
identifier can be named.  Since these two conventions are not
necessarily the same, it is necessary to give each file an external
name which the operating system is happy with, and an internal
filename which Pascal is happy with.  It now becomes necessary to
tie these two names together, and this is the primary job of the
Assign statement.

Now that we have a file identified, it is necessary to prepare it
for reading by executing a reset statement in line 9.  The Reset
statement positions the read pointer at the beginning of the file,
ready to read the first piece of information in the file.  Once we
have done that, data is read from the file in the same manner as
it was when reading from the keyboard.  In this program, the input
is controlled by the while loop which is executed until we exhaust
the data in the file.

                                                        Page 11-2

                                   Chapter 11 - File Input/Output

WHAT ARE THE "EOF" AND "EOLN" FUNCTIONS?
_________________________________________________________________

The Eof function is new and must therefore be defined.  When we
read data from the file, we move closer and closer to the end of
the file, until finally we reach the end and there is no more data
to read.  This is called "end of file" and is abbreviated Eof. 
Pascal has this function available as a part of the standard
library which returns FALSE until we reach the last line of the
file.  When there is no more data to read left in the file, the
function Eof returns TRUE.  To use the function, we merely give it
our file identifier as an argument.  It should be clear to you that
we will loop in this program until we read all of the data
available in the input file.

The Eoln function is not used in this program but is a very useful
function.  If the input pointer is anywhere in the text file except
at the end of a line, the Eoln returns FALSE, but at the end of a
line, it returns a value of TRUE.  This function can therefore be
used to find the end of a line of text for variable length text
input.

To actually read the data, we use the Readln procedure, giving it
our identifier Turkey and the name of the variable we want the data
read into.  In this case, we read up to 80 characters into the
string and if more are available, ignore them.  You should remember
when we did this in the last chapter from the keyboard input.  We
are using the same technique here except we are reading from a file
this time.  Since we would like to do something with the data, we
output the line to the default device, the video monitor.  It
should be clear to you by now that the program will read the entire
file and display it on the monitor.

Finally, we Close the file Turkey.  It is not really necessary to
close the file because the system will close it for us
automatically at program termination, but it is a good habit to get
into.  It must be carefully pointed out here, that you did not do
anything to the input file, you only read the data and left it
intact.  You could Reset it and reread it again in this same
program.  Compile and run this program to see if it does what you
expect it to do.


A PROGRAM TO READ ANY FILE
_________________________________________________________________

Examine the next program READDISP.PAS for an     ================
improved file reading program.  This is very       READDISP.PAS
similar to the previous program, except that it  ================
asks you for the name of the file which you wish
to display.  After you enter the filename, it
enters the name into a 12 character string  which is named
Name_Of_File_To_Input which will be the external filename.  This
is then used in the Assign statement to select the file to be read,

                                                        Page 11-3

                                   Chapter 11 - File Input/Output

and to connect the external filename to the internal filename,
Chicken.  The file is then reset as before.  Lines 15 through 18
display a header, and from that point on, the program is identical
to the last one with a few small additions.  In order to
demonstrate the use of a function within the Writeln specification,
the program calls for the length of the input string in line 23 and
displays it before each line.  The lines are counted as they are
read and displayed, and the line count is displayed at the end of
the listing.  Both of these operations are done only to illustrate
to you how they can be done. 

You should be able to understand clearly how each of these
operations is accomplished.  Compile and run this program, entering
any filename we have used so far as the file to be listed on the
monitor (be sure to include the .PAS extension).  After a
successful run, enter a nonexistent filename and see the I/O error
generated by the Pascal runtime system.  The next example program
will illustrate a more graceful method of detecting this error.


HOW TO COPY A FILE (SORT OF)
_________________________________________________________________

Examine the file READSTOR.PAS for an example of  ================
reading from a file and writing to another one.    READSTOR.PAS
In this program we request an operator input for ================
the filename to read, after which we Assign the
name to the file and Reset it.  When we reset
the file however, we go to a bit of extra trouble to assure that
the file actually exists.  Note that this is an extension to TURBO
Pascal and will probably not work with other Pascal compilers.

Suppose we input a filename, and the file did not exist because the
file was actually missing, or because we entered the name of the
file wrong.  Without this extra effort, the TURBO Pascal runtime
system would indicate a run-time error, and terminate the program
returning us to the operating system.  In order to make a program
easier to use, it would be nice to tell the operator that the file
didn't exist and give him the opportunity to try again with another
file name.  The method given in lines 16 through 20 of this program
will allow you to do just that.


USING A COMPILER DIRECTIVE
_________________________________________________________________

First you must disable the built in TURBO Pascal I/O checking by
inserting the compiler directive in line 16.  This tells the system
to ignore any I/O errors from this point on and if the file doesn't
exist, the system will not abort when you attempt to reset it in
line 17.  Another compiler directive is given in line 18 to enable
I/O checking again for the remainder of the program.  



                                                        Page 11-4

                                   Chapter 11 - File Input/Output


WE DO OUR OWN FILE CHECKING
_________________________________________________________________

If the file didn't exist and could not therefore be reset, we have
a problem because the program thinks the file is available for use
but it actually isn't.  Fortunately, TURBO Pascal has a built in
variable, named IOResult that informs us of the result of each I/O
operation.  Following any I/O operation, if this variable contains
the value of zero, the I/O operation was correct, and if it
contains any other value, the operation had some sort of error. 
In our case, we simply compare it to zero to generate a boolean
value, then based on the boolean value we either give an error
message and stop, or perform the desired file operations.

It would be good programming practice to check all file openings
in this manner to allow the operator to recover from a simple
oversight or spelling error.

If the file was opened properly, then in line 21 through 24 we
request a different filename to write to, which is assigned to a
different identifier.  Note that the output file is not checked for
a valid opening in this example as it should be.  The statement in
line 24 is new to us, the Rewrite statement.  This name apparently
comes from the words REset for WRITEing because that is exactly
what it does.  It clears the entire file of any prior data and
prepares to write into the very beginning of the file.  Each time
you write into it, the file grows by the amount of the new data
written.

Once the identifier has been defined, and the Rewrite has been
executed, writing to the file is identical to writing to the
display with the addition of the identifier being specified prior
to the first output field.  With that in mind, you should have no
trouble comprehending the operation of the program.  This program
is very similar to the last, except that it numbers the lines as
the file is copied.  After running the program, look in your
default directory for the new filename which you input when the
system asked for the output filename.  Examine that file to see if
it is truly a copy of the input file with line numbers added.  

Actually a much better style would result if the logic for each
file opening was put into a procedure of its own.  In addition to
this being easier to debug and understand, the completed procedures
could be reused in other programs in the future.

One word of caution.  If you used an existing filename for the
output file, the file was overwritten, and the original destroyed. 
In that case, it was good that you followed instructions at the
beginning of this tutorial and made a working copy of the
distribution disk.  You did do that, didn't you?

Compile and run this program two different ways, once with a valid
input filename that should run properly, and the second time with

                                                        Page 11-5

                                   Chapter 11 - File Input/Output

an input filename that doesn't exist to prove to yourself that the
test actually does work correctly.


HOW TO READ INTEGER DATA FROM A FILE
_________________________________________________________________

It is well and good to be able to read text from a file, but now
we would like to read other forms of data from a file.  First we
will look at an example program to read data from a text file, then
later we will see an example program that reads from a binary file. 

Examine the program READINTS.PAS for an example  ================
of reading data from a text file.  A text file     READINTS.PAS
is an ASCII file that can be read by a text      ================
editor, printed, displayed, or in some cases,
compiled and executed.  It is simply a file made
up of a long string of char type data, and usually includes
linefeeds, carriage returns, and blanks for neat formatting. 
Nearly every file on the Tutorial disk you received with this
package is a text file.  One notable exception is the file named
LIST.EXE, which is an executable program file.

The example program has nothing new, you have seen everything in
it before.  We have an assignment, followed by a reset of our file,
followed by four read and write loops.  Each of the loops has a
subtle difference to illustrate the Read and Readln statements. 
Notice that the same file is used for reading four times with a
Reset prior to each, illustrating the nondestructive read mentioned
a few paragraphs ago.

The file we will be using is named INTDATA.TXT and is on your disk. 
You could display it at this time using the program READDISP.PAS
we covered recently.  Notice that it is simply composed of the
integer values from 101 to 148 arranged four to a line with a
couple of spaces between each for separation and a neat appearance. 
The important thing to remember is that there are four data points
per line.


READ AND READLN ARE SLIGHTLY DIFFERENT
_________________________________________________________________

As variables are read in with either procedure, the input file is
scanned for the variables using blanks as delimiters.  If there are
not enough data points on one line to satisfy the arguments in the
input list, the next line is searched also, and the next, etc. 
Finally when all of the arguments in the input list are satisfied,
the Read is complete, but the Readln is not.  If it is a Read
procedure, the input pointer is left at that point in the file, but
if it is a Readln procedure, the input pointer is advanced to the
beginning of the next line.  The next paragraph should clear that
up for you.


                                                        Page 11-6

                                   Chapter 11 - File Input/Output

The input data file INTDATA.TXT has four data points per line but
the first loop in the program READINTS.PAS requests only three each
time through the loop.  The first time through, it reads the values
101, 102, and 103, and displays those values, leaving the input
pointer just prior to the 104, because it is a Read procedure.  The
next time through, it reads the value 104, advances to the next
line and reads the values 105, and 106, leaving the pointer just
prior to the 107.  This continues until the 5 passes through the
loop are completed.

The loop in lines 19 through 22 contains a Readln procedure and
also reads the values 101, 102, and 103, but when the input
parameter list is satisfied, it moves the pointer to the beginning
of the next line, leaving it just before the 105.  The values are
printed out and the next time we come to the Readln, we read the
105, 106, and 107, and the pointer is moved to the beginning of the
next line.  It would be good to run the program now to see the
difference in output data for the two loops.  Remember that the
only difference is that the first loop uses the Read procedure, and
the second uses the Readln procedure.

When you come back to the program again, observe the last two
loops, which operate much like the first two except that there are
now five requested integer variables, and the input file still only
has four per line.  This is no problem.  Both input procedures will
simply read the first four in the first line, advance to the second
line for its required fifth input, and each will do its own
operation next.  The Read procedure will leave the input pointer
just before the second data point of the second line, and the
Readln will advance the input pointer to the beginning of the third
line.  Compile and run this program and observe the four output
fields to see an illustration of these principles.


NOW TO READ SOME REAL VARIABLES FROM A FILE
_________________________________________________________________

Examine the file named REALDATA.TXT supplied on your Pascal
Tutorial disk.  You will see 8 lines of what appears to be
scrambled data, but it is good data that Pascal can read.  Notice
especially line 4 which has some data missing, and line 6 which has
some extra data.

Examine the program file READDATA.PAS which will ================
be used to illustrate the method of reading real   READDATA.PAS
type data.  Everything should be familiar to     ================
you, since there is nothing new here.  The
Readln statement is requesting one integer
variable, and three real variables, which is what most of the input
file contains.  When we come to the fourth line, there are not
enough data points available, so the first two data points of the
next line are read to complete the fourth pass through the loop. 
Since the file pointer is advanced to the beginning of the next
line, we are automatically synchronized with the data again.  When

                                                        Page 11-7

                                   Chapter 11 - File Input/Output

we come to the sixth line, the last two data points are simply
ignored.  Run the program to see if the results are as you would
predict.

If a Read were substituted for the Readln in line 14 of the
program, the file pointer would not be advanced to the beginning
of line 6 after the fourth pass through the loop.  The next attempt
to read would result in trying to read the value 0.0006 as an
integer, and a run time error would result.  Modify the program,
substituting a Read for the Readln in line 14, and see if this is
not true.  It will be left as an exercise for the diligent student
to add code to detect and act on the error in a manner similar to
that illustrated in READSTOR.PAS earlier in this chapter.

It should be pointed out that TURBO Pascal requires a digit both
before and after the decimal point in all data that is to be read
in as real type data or it will be flagged as a run-time error and
the program will be halted.  The digits can be zero as they are in
several places in the example file but they must be there.

That is all there is to reading and writing text files.  If you
learn the necessities, you will not be stumbling around in the area
of input/output which is very intimidating to many people. 
Remember to Assign, then Reset before reading, Rewrite before
writing, and Close before quitting.  It is of the utmost importance
to close a file you have been writing to before quitting to write
the last few buffers to the file, but it is not as important to
close read files unless you are using a lot of them, as there is
an implementation dependent limit of how many files can be open at
once.  It is possible to read from a file, close it, reopen it, and
write to it in one program.  You can reuse a file as often as you
desire in a program, but you cannot read from and write into a file
at the same time.



NOW FOR BINARY INPUT AND OUTPUT
_________________________________________________________________

Examine the file BINOUT.PAS for an example of    ================
writing data to a file in binary form.  First       BINOUT.PAS
there is a record defined in the type            ================
declaration part composed of three different
variable types.  In the var part, Output_File is
defined as a "file of Dat_Rec", the record defined earlier.  The
variable Dog_Food is then defined as an array of the record, and
a simple variable is defined.

Any file assigned a type of text, which is a "file of char", is a
text file.  A text file can be read and modified with a text
editor, printed out, displayed on the monitor, etc. If a file is
defined with any other definition, it will be a binary file and
will be in an internal format as defined by the Pascal compiler and
may not be readable by any compiler other than the one used to

                                                        Page 11-8

                                   Chapter 11 - File Input/Output

write it.  Attempting to display such a file will result in very
strange looking gibberish on the monitor.

When we get to the program, the output file is assigned a name in
line 15, and a Rewrite is performed on it to reset the input
pointer to the beginning of the file, empty the file, and prepare
for writing data into it.  The loop in lines 18 through 22 simply
assigns nonsense data to all of the variables in the 20 records so
we have something to work with.

We write a message to the display that we are ready to start
outputting data, then we output the data one record at a time with
the standard Write statement.  A few cautions are in order here. 
The output file can be defined to store any simple variable type,
integer, byte, real, or a record, but the types cannot be mixed. 
The record itself however, can be any combination of data including
other records if desired, but any file can only have one type of
record written to it.  

A Writeln statement is illegal when writing to a binary file
because a binary file is not line oriented.  A Write statement is
limited to one output field per statement.  This is not a serious
limitation since it is a simple matter to put one Write statement
in the program for each variable you wish to write out to the file. 
It is important to Close the file when you are finished writing to
it.


WHY USE A BINARY FILE
_________________________________________________________________

A binary file written by a Pascal program cannot be read by a word
processor, a text editor or any other application program such as
a database or spreadsheet, and it may not even be readable by a
Pascal program compiled by a different companies compiler because
the actual data structure is implementation dependent.  It can't
even be read by a Pascal program using the same compiler unless the
data structure is identical to the one used to write the file. 
With all these rules, it seems like a silly way to output data, but
there are advantages to using a binary output.

A binary file uses less file space than a corresponding text file
because the data is stored in a packed mode.  Since all significant
digits of real data are stored, it is more precise unless you are
careful to output all significant data to the corresponding text
file.  Finally, since the binary data does not require formatting
into ASCII characters, it will be considerably faster than
outputting it in text format.  When you run this example program,
it will create the file KIBBLES.BIT, and put 20 records in it. 
Return to DOS and look for this file and verify its existence.  If
you try to TYPE it, using the DOS TYPE command, you will have a
real mess on your monitor because it does not contain char type
data, but that might be a good exercise.


                                                        Page 11-9

                                   Chapter 11 - File Input/Output

READING A BINARY FILE
_________________________________________________________________

BININ.PAS is another example program that will    ===============
read in the file we just created.  Notice that       BININ.PAS
the variables are named differently, but the      ===============
types are all identical to those used to write
the file and they are in the same order.  An
additional line is found in the program, the if statement.  We must
check for the "end of file" marker to stop reading when we find it
or Pascal will list an error and terminate operation.  Three pieces
of information are written out to verify that we actually did read
the data file in.

Once again, a few rules are in order.  A Readln is illegal since
there are no lines in a binary file, and only one variable or
record can be read in with each Read statement.


FILE POINTERS, GET, AND PUT STATEMENTS
_________________________________________________________________

File pointers and the Get and Put procedures are a part of standard
Pascal, but since they are redundant and therefore not needed, they
are not a part of TURBO Pascal.  The standard Read and Write
procedures are more flexible, more efficient, and easier to use. 
The use of Get and Put will not be illustrated or defined here. 
If you ever have a need for them, they should be covered in detail
in your Pascal reference manual for the particular implementation
you are using.

Pointers will be covered in detail in the next chapter of this
tutorial.


PROGRAMMING EXERCISES
_________________________________________________________________

1.   Modify READFILE.PAS so that after reading and displaying the
     file, the file is reset, then read and displayed again.  This
     was suggested in the text.

2.   Write a program to read the data from any text file, and
     display it on the monitor with line numbers and the number of
     characters in each line. Finally display the number of lines
     found in the file, and the total number of characters in the
     entire file. Compare this number with the filesize given by
     the DOS command DIR.







                                                       Page 11-10
