
                           CHAPTER 11 - Files


             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 compilers, and all that is covered
        will  apply to the TURBO Pascal compilers, versions 3.0  and
        4.0.   If you are not using TURBO Pascal and  your  compiler
        complains about some of the statements, it will be up to you


                                 Page 63









                           CHAPTER 11 - Files


        to dig out the details of how to do the intended operations.
        If   any  one  or  more  of  these  operations   cannot   be
        accomplished  with  your  compiler,  you  should   seriously
        consider  purchasing a better compiler because all of  these
        operations are needed in a useful Pascal environment.

                       READING AND DISPLAYING A FILE

             Examine  the file READFILE for an example of a  program
        that  can read a text file from the disk.  In fact  it  will
        read  itself  from  the disk and display  it  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.   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.

             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.

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

             The  Eof function is new and must be defined.  When  we
        read  data from the file, we move closer and closer  to  the
        end,  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



                                 Page 64









                           CHAPTER 11 - Files


        an argument.  It should be clear that we will loop 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  you 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  it 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 for an improved  file
        reading  program.  This is very similar except that it  asks
        you for the name of the file that you desire to display, and
        enters   the   name  into  a  12  character   string   named
        Name_Of_File_To_Input.   This  is then used  in  the  Assign
        statement  to  select the file to be read, and the  file  is
        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.




                                 Page 65









                           CHAPTER 11 - Files


             You  should  be able to see clearly how each  of  these
        operations  is accomplished.  Compile and run this  program,
        entering  any  filename  we have used so  far  (be  sure  to
        include the .PAS extension).  After a successful run,  enter
        a nonexistent filename and see the I/O error.

                       HOW TO COPY A FILE (SORT OF)

             Examine  the  file  READSTOR for  an  example  of  both
        reading  from  a file and writing to another one.   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.

             Suppose we input a filename, and the file did not exist
        because the file was actually missing, or because we entered
        the  filename  wrong.  Without the 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.

                        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.



                                 Page 66









                           CHAPTER 11 - Files


             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 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 it asked
        for the output filename.  Examine that file to see if it  is
        truly a copy of the input file with line numbers added.

             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 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 for an example of  reading
        data  from a text file.  A text file is an ASCII  file  that


                                 Page 67









                           CHAPTER 11 - Files


        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.COM, 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  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.

             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.




                                 Page 68









                           CHAPTER 11 - Files


             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.   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

             By whatever method you desire, take a look at 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 which will be used to
        illustrate the method of reading real 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
        contained.   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 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.


                                 Page 69









                           CHAPTER 11 - Files


        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 should be pointed out that TURBO Pascal 4.0 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.  If you are using TURBO
        Pascal  3.0,  the  leading  and  trailing  digits  are   not
        required.

             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 for an example of writing  data
        to  a file in binary form.  First 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.  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


                                 Page 70









                           CHAPTER 11 - Files


        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, and 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  as  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.   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, but that might be a good
        exercise.



                                 Page 71









                           CHAPTER 11 - Files


                           READING A BINARY FILE

             BININ is another example program that will read in  the
        file  we just created.  Notice that the variables are  named
        differently,  but the types are all identical to those  used
        to  write  the  file.  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.

            WHAT ABOUT 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, 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 any 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.  Write a program to read in 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.

        2.  Write  a silly program that will read two text files and
            display  them both on the monitor on alternating  lines.
            This is the same as "shuffling" the two files  together.
            Take  care  to  allow them to end  at  different  times,
            inserting  blank  lines  for the  file  that  terminates
            earlier.







                                 Page 72
