2-9  FORMATTED / LIST-DIRECTED / UNFORMATTED I/O 
 ************************************************

 A small glossary
 ----------------
   I/O STATEMENT       WRITE (UNIT=10, FMT='(1X,A,I6)', END=999) ' i= ', i
   CONTROL LIST        (UNIT=10, FMT='(1X,A,I6)', END=999) 
   SPECIFIERS:
      FORMAT           FMT='(1X,A,I6)', FMT=*
      RECORD           REC=nrecord    (Only for files opened DIRECT) 
      ERROR            ERR=999
      END-OF-FILE      END=999        (Not for files opened DIRECT) 
      I/O STATUS       IOSTAT=intvar  (Values are implementation-dependant)
   I/O LIST            ' i= ', i      (should be compatible with the format)


 Formatted vs. unformatted - two ways to store numbers
 -----------------------------------------------------
 There are two different ways to represent integer and floating 
 point numbers: 

   1) UNFORMATTED - is just the binary representation the computer 
      uses in the CPU registers and in the main memory. Such files
      can be read/written efficiently because no translations are 
      needed, they also take less space on disk. 

   2) FORMATTED representation is the sequence of characters used to
      describe the number in some radix (usually 10), it may contain 
      the ten digits and few more characters like '+', '-', or 'E'.

 For example, the integer 1024 may be represented as:

       3                   2                   1
     1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0|0|0|0|0|0|0|0|0|
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    Unformatted representation


 We assume here INTEGER*4 data type. remember that: 1024 = 2 ** 10


     7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0   7 6 5 4 3 2 1 0
    +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
    |0|0|1|1|0|0|0|1| |0|0|1|1|0|0|0|0| |0|0|1|1|0|0|1|0| |0|0|1|1|0|1|0|0|
    +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
    Formatted representation

 The four digits '1', '0', '2', '4' are kept each in a byte, the 
 bytes are ordered here from left to right. 

 An excerpt from the ASCII table will help here:

            Character   ASCII value        ASCII in binary
            ---------   -----------        ---------------
              0             48                 00110000
              1             49                 00110001
              2             50                 00110010
              3             51                 00110011
              4             52                 00110100
              5             53                 00110101
              6             54                 00110110
              7             55                 00110111
              8             56                 00111000
              9             57                 00111001

              +             43                 00101011
              -             45                 00101101
              E             69                 01000101
    
    Floating point numbers will need '+', '-', 'E'

 A formatted file contents are a sequence of printable characters 
 which may be displayed on the screen, or viewed with a text editor.  
 An unformatted file contain sequences of compiler/machine dependent 
 representations of data values, and is not suitable for viewing.

 Note that for character data there is no difference between the 
 formatted and unformatted methods of recording data.


 The three I/O methods
 ---------------------
 FORTRAN I/O is record-oriented, see the chapter on files and records 
 for more info on records and the structure of files.

 There are three I/O processing methods (not record attributes):

    1)  UNFORMATTED -    Data is copied between file and memory as is.
    2)  FORMATTED -      Radix conversion (decimal-binary) and editing
    3)  LIST-DIRECTED -  Like formatted, but with default formats

 When reading/writing unformatted files it is enough to specify the
 variables you wish to read/write.  Since you are using the compiler-
 defined internal representations, the compiler has all information.

 Reading/Writing formatted files requires specifying more information,
 e.g. how many digits should be displayed for the mantissa of a REAL
 number, the solution adopted by many programming languages is using 
 a FORMAT SPECIFICATION.


   Comparison of the three I/O methods
   ===================================

 INPUT/OUTPUT characteristics:
 ---------------|--------------------|-------------------|---------------------
                |   List-directed    |    Formatted      |   Unformatted
 ---------------|--------------------|-------------------|---------------------
 Syntax         | FMT=*              | FMT='(format)'    | No format 
                |                    |                   | specifier is used
 ---------------|--------------------|-------------------|---------------------
 Type of        | Printable chars    | Printable chars   | Binary, like in 
 representation | only, radix 10     | only, radix 10    | registers & memory
 ---------------|--------------------|-------------------|---------------------
 Record         | Sequence of        | Sequence of       | Sequence of internal
 content        | characters         | characters        | representations
 ---------------|--------------------|-------------------|---------------------
 Natural unit   | Character storage  | Character storage | Numeric/character
 of size        | unit, usually byte | unit, usually byte| storage unit
 ---------------|--------------------|-------------------|---------------------
 CPU time       | High               | High              | Low
 consumption    |                    |                   | 
 ---------------|--------------------|-------------------|---------------------
 File size      | Larger             | Larger            | Smaller
                |                    |                   | 
 ---------------|--------------------|-------------------|---------------------

 (continued...)
 ---------------|--------------------|-------------------|---------------------
                |   List-directed    |    Formatted      |   Unformatted
 ---------------|--------------------|-------------------|---------------------
 Precision loss | Yes                | Yes               | No
                |                    |                   | 
 ---------------|--------------------|-------------------|---------------------
 Separators     | Blank (Space)      | No, pre-determined| No, pre-determined
 in input?      | Comma              | offsets in record | offsets in record
 ---------------|--------------------|-------------------|---------------------
 Multi-record   | Yes, automatic     | Yes, with a '/'   | No, one record
 Input/output   | and manual         | or format re-scan | for each statement
 ---------------|--------------------|-------------------|---------------------
 File opened    |                    | Allowed           | Not allowed
 formatted      |                    |                   | 
 ---------------|--------------------|-------------------|---------------------
 File opened    |                    | Not allowed       | Allowed
 unformatted    |                    |                   | 
 ---------------|--------------------|-------------------|---------------------
 Internal files | No                 | In sequential     | No
 are allowed?   |                    | access mode       | 
 ---------------|--------------------|-------------------|---------------------
 Direct files   | No                 | Yes               | Yes
 are allowed?   |                    |                   | 
 ---------------|--------------------|-------------------|---------------------


   INPUT characteristics:
   ======================

 ---------------|--------------------|-------------------|---------------------
                |   List-directed    |    Formatted      |   Unformatted
 ---------------|--------------------|-------------------|---------------------
 Character      | Should be enclosed | No quotes needed  | No quotes needed
 input quoted?  | in quotes          |                   |
 ---------------|--------------------|-------------------|---------------------
 Null values    | Yes, corresponding |                   | 
 are supported  | var doesn't change |                   | 
 ---------------|--------------------|-------------------|---------------------
 Repeat-counts  | Yes, for constants |                   | 
 are allowed    | and null values    |                   | 
 ---------------|--------------------|-------------------|---------------------
 Blank          | Yes, except within |                   | 
 Collapsing     | character constant |                   |
 ---------------|--------------------|-------------------|---------------------
 Auto-skip of   |                    |                   |
 empty records  |                    |                   |
 ---------------|--------------------|-------------------|---------------------


   OUTPUT characteristics:
   =======================

 ---------------|--------------------|-------------------|---------------------
                |   List-directed    |    Formatted      |   Unformatted
 ---------------|--------------------|-------------------|---------------------
 Carriage       | Prefixes a blank   | Chops first char. | Carriage-control
 control        | before each record | sent to 'printers'| is inactive
 ---------------|--------------------|-------------------|---------------------
 Repeat-counts  | May be implemented |                   | 
 are allowed    |                    |                   | 
 ---------------|--------------------|-------------------|---------------------
 Values cut at  | Starts on a new    |                   | No
 end of record  | line if it helps   |                   |
 ---------------|--------------------|-------------------|---------------------
 Blank          |                    |                   | 
 Collapsing     |                    |                   |
 ---------------|--------------------|-------------------|---------------------
 Auto-skip of   |                    |                   |
 empty records  |                    |                   |
 ---------------|--------------------|-------------------|---------------------
               


   List-directed (free-field) I/O  vs. the other methods
   =====================================================

     Advantages                       |  Disadvantages
    ----------------------------------|-------------------------------------
    Flexible input - values don't     | Slower than unformatted 
    have to be at fixed offsets       | 
    from the record beginning         | 
    ----------------------------------|-------------------------------------
    Simplicity - gives nice results   | Larger files than in unformatted
    with minimal effort               | 
    ----------------------------------|-------------------------------------
    Accepts repeat-counts and null    | Some precision is lost on
    values (leave corresponding       | internal/ASCII conversions
    variable unchanged)               |
    ----------------------------------|-------------------------------------
    Accepts two types of value-       | Character input requires 
    delimiters: COMMA and SPACE       | apostrophe delimiting
    ----------------------------------|-------------------------------------
    Portable output                   | Internal files not allowed
                                      | 
    ----------------------------------|-------------------------------------
    Record spanning - reading/writing | Direct files not allowed
    automatically advances to next    |
    record if needed, no exceptions   |
    are generated                     |
    ----------------------------------|-------------------------------------


   Overflowing and underflowing I/O transfers
   ==========================================
 
 SEQUENTIAL ACCESS METHOD
 -------------------|------------------------|-------------------------
                    | Record length smaller  | Record length larger 
                    | than I/O list size     | than I/O list size 
                    | [and format size]      | [and format size]
 ===================|========================|=========================
 List-directed      | Automatically starts   |
 input              | a new record           |
 -------------------|------------------------|-------------------------
 List-directed      | Automatically starts   |
 output             | a new record           |
 ===================|========================|=========================
 Formatted input    | Not allowed            |
                    |                        |
 -------------------|------------------------|-------------------------
 Formatted output   | Not allowed            | Padding with blanks
                    |                        | (SPACE characters)
 ===================|========================|=========================
 Unformatted input  | Not allowed            | Ignores rest of record
                    |                        | 
 -------------------|------------------------|-------------------------
 Unformatted output |                        | 
                    |                        | 
 ===================|========================|=========================


 DIRECT ACCESS METHOD
 -------------------|------------------------|-------------------------
                    | Record length smaller  | Record length larger 
                    | than I/O list size     | than I/O list size 
                    | [and format size]      | [and format size]
 ===================|========================|=========================
 List-directed      | Not allowed            | Not allowed
 input              |                        |
 -------------------|------------------------|-------------------------
 List-directed      | Not allowed            | Not allowed
 output             |                        |
 ===================|========================|=========================
 Formatted input    |                        |
                    |                        |
 -------------------|------------------------|-------------------------
 Formatted output   | Not allowed            | Padding with blanks
                    |                        | (SPACE characters)
 ===================|========================|=========================
 Unformatted        |                        | 
 input              |                        |
 -------------------|------------------------|-------------------------
 Unformatted        | Not allowed            | Rest of record    
 output             |                        | contains garbage
 ===================|========================|=========================



 An example program:

      PROGRAM READFL
C     ------------------------------------------------------------------
      INTEGER
     *              I, SMALL(5), BIG(15)
C     ------------------------------------------------------------------
      OPEN( UNIT =              10,
     *      FILE =              'tmp.inp',
     *      ACCESS =            'SEQUENTIAL',
     *      STATUS =            'OLD',
     *      FORM =              'FORMATTED')
C     ------------------------------------------------------------------
      WRITE (*,*)
      WRITE (*,'(6X,A)') ' Reading less than a record (Formatted): '
      WRITE (*,'(6X,A)') ' ======================================= '
C     ------------------------------------------------------------------
      DO I = 1, 3
        READ(UNIT=10, FMT='(5I4)')    SMALL
        WRITE(UNIT=*, FMT='(6X,5I4)') SMALL
      ENDDO
C     ------------------------------------------------------------------
      WRITE (*,*)
      WRITE (*,'(6X,A)') ' Reading less than a record (List-directed): '
      WRITE (*,'(6X,A)') ' =========================================== '
      REWIND(UNIT=10)
C     ------------------------------------------------------------------
      DO I = 1, 3
        READ(UNIT=10, FMT=*)          SMALL
        WRITE(UNIT=*, FMT='(6X,5I4)') SMALL
      ENDDO
C     ------------------------------------------------------------------
      WRITE (*,*)
      WRITE (*,'(6X,A)') ' Reading more than a record (Formatted): '
      WRITE (*,'(6X,A)') ' ======================================= '
      REWIND(UNIT=10)
C     ------------------------------------------------------------------
      DO I = 1, 3
        READ(UNIT=10, FMT='(15I4)')    BIG
        WRITE(UNIT=*, FMT='(6X,15I4)') BIG
      ENDDO
C     ------------------------------------------------------------------
      WRITE (*,*)
      WRITE (*,'(6X,A)') ' Reading more than a record (List-directed): '
      WRITE (*,'(6X,A)') ' =========================================== '
      REWIND(UNIT=10)
C     ------------------------------------------------------------------
      DO I = 1, 3
        READ(UNIT=10, FMT=*)           BIG
        WRITE(UNIT=*, FMT='(6X,15I4)') BIG
      ENDDO
C     ------------------------------------------------------------------
      END


 The input file for this example is:

11, 12, 13, 14, 15, 16, 17, 18, 19 
21, 22, 23, 24, 25, 26, 27, 28, 29 
31, 32, 33, 34, 35, 36, 37, 38, 39 
41, 42, 43, 44, 45, 46, 47, 48, 49 
51, 52, 53, 54, 55, 56, 57, 58, 59 
61, 62, 63, 64, 65, 66, 67, 68, 69 
71, 72, 73, 74, 75, 76, 77, 78, 79 
81, 82, 83, 84, 85, 86, 87, 88, 89 
91, 92, 93, 94, 95, 96, 97, 98, 99 


 A possible output of the example program:


       Reading less than a record (Formatted): 
       ======================================= 
        11  12  13  14  15
        21  22  23  24  25
        31  32  33  34  35

       Reading less than a record (List-directed): 
       =========================================== 
        11  12  13  14  15
        21  22  23  24  25
        31  32  33  34  35

       Reading more than a record (Formatted): 
       ======================================= 
        11  12  13  14  15  16  17  18  19   0   0   0   0   0   0
        21  22  23  24  25  26  27  28  29   0   0   0   0   0   0
        31  32  33  34  35  36  37  38  39   0   0   0   0   0   0

       Reading more than a record (List-directed): 
       =========================================== 
        11  12  13  14  15  16  17  18  19  21  22  23  24  25  26
        31  32  33  34  35  36  37  38  39  41  42  43  44  45  46
        51  52  53  54  55  56  57  58  59  61  62  63  64  65  66



 Mixing "formatted" and "unformatted" I/O
 ----------------------------------------
 Formally these are incompatible I/O processing methods, and should
 never be mixed in the same file, however it is useful to include a 
 textual header at the beginning of an unformatted file, and there 
 is a simple workaround technique to do it. 

 This technique can be used to store in the first records of an 
 unformatted file all the information needed to read the file on 
 a different machine: internal structure, type of floating-point
 numbers used, etc.

 Unformatted files may contain text, as it is just character data, 
 but some file-viewing programs don't "like" unformatted files, 
 because they may contain control bytes that can't be interpreted 
 as printable characters. 

 It's difficult to arrange that these first records can be viewed by 
 all file-viewing programs, without displaying "garbage", or causing 
 undesirable effects to the terminal.  A FORTRAN routine can easily 
 do it, and another one can write this "header records" (see the
 examples appendix).


 Advancing and non-advancing I/O
 -------------------------------
 FORTRAN 77 I/O is always advancing, i.e. every WRITE statement
 starts a new record, and every READ statement starts reading
 at the beginning of the next record, even if the preceding 
 READ didn't 'exhausted' the preceding record. 

 With non-advancing I/O, WRITE and READ continue in the 
 current record, until explicitly told to start a new one.
 To make FORTRAN 77 formatted I/O non-advancing you need a 
 library of buffering routines.

 Fortran 90 supports both I/O types.



 Status values returned by the IOSTAT specifier
 ----------------------------------------------
 IOSTAT values are not standard, as I/O errors are implementation 
 dependant, the standard says only that no-error has a zero value, 
 and the error conditions have strictly positive values.

 One advantage of using the IOSTAT specifier over the ERR and END 
 specifiers, is that you can use a block IF condition to deal with 
 the status returned, for example:

      OPEN(...., IOSTAT=ios, ....)
      IF (ios .GT. 0) THEN
        WRITE(*,*) ' OPEN error no. ', ios
        STOP 
      ELSE
        WRITE(*,*) ' File was opened... '
      ENDIF

 The disadvantage of this method is that you lose the diagnostic
 message from the I/O run-time library, and have to translate
 yourself the error number, a system-dependant process.


 Sometimes you need to know the IOSTAT values in order to write 
 a program (non-portable) that can gracefully handle various
 error conditions.

 You can find IOSTAT values in:

    SYS$LIBRARY:FORIOSDEF.FOR             (OpenVMS)
    the perror manpage                    (IRIX)

 Remember that if you use IOSTAT you give up the protection of the
 compiler, if an I/O error occurs, the variables you tried to read 
 may be trashed and unusable, and even your position in the file 
 may be lost and you will have to find it again. Having used IOSTAT 
 the program will not abort and it's up to you to handle the situation.



Return to contents page