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