Control over the appearance of output is via the format specifier,
which is a string. This similar in spirit to the format string
provided to printf()
in C. The use of a format allows predictable
tabulation of output for human readers.
So far we have seen only output (via either print
or write
). Input
is via the read
statement.
use :: iso_fortran_env
...
read (unit = input_unit, fmt = *) var-io-list
In very many respects, read
looks much like write
, in that it
takes the same arguments, including unit number and format specifier.
However, whereas write
may have an I/O list involving expressions, read
must have only variables.
The standard input unit (input_unit
from iso_fortran_env
in the above)
is typically the keyboard (or "screen") for interactive use.
In addition to the list-directed or free-format *
specifier, a
format may be a string literal, a fixed character parameter, or a
string constructed at run time. Some examples are:
read (*, *) var ! read using list-directed, or free format
print '(a)', "A string" ! character literal '(a)'
write (*, myformat) var ! character variable (or parameter)
write(unit = myunit) var ! unformatted (no format)
Output with string (or *
) format specifier is referred to as formatted I/O.
This is to distinguish it from unformatted I/O; this is essentially a
direct dump of the binary internal representation. For unformatted
I/O the format specifier is simply omitted.
Binary output can have its uses, but more portable and flexible methods of "fast" output are probably to be preferred (e.g., via a library such as HDF5). We will not consider unformatted output any further in this course.
A format specifier consists of a comma-separated list of edit descriptors enclosed in parentheses. The edit descriptors determine how the internal representation of data is translated or converted to characters which can be displayed.
The formal and exhaustive statement of exactly what is allowed in an edit descriptor is rather, well, exhausting. It is easier to consider some common examples of the three different types of descriptor.
Data edit descriptors feature a single character describing the type data format wanted, and a literal integer part indicating the total field width, and number of decimals. These include
iw ! integer width w characters
fw.d ! floating point number total width w and d decimal characters
ew.d ! scientific notation with total width w and d decimal characters
aw ! character string total width w
The width is the total width of the field, and must be wide enough to accommodate any leading signs, decimal points, or exponents. Where there is a w.d the d indicates the number of digits to appear after the decimal point.
Data edit descriptors should correspond to the relevant item in the io-list, based on position. Some simple examples are:
integer :: ivar = 40
real :: avar = 40.0
print "( i10)", ivar ! produces "40" with 8 leading spaces
print "(f10.3)", avar ! produces "40.000" with 4 leading spaces
print "(e10.3)", avar ! produces "0.400e+02" with 1 leading space
For scientific notation, the exponent will appear either with two digits ("E+dd" or "E-dd"), or three digits for exponents greater than 99 ("+ddd" or "-ddd").
If a first significant digit is required before the decimal point in
scientific notation (instead of a zero) the es
edit descriptor can
be used instead of e
; otherwise it's the same.
An engineering edit descriptor en
is available. It also acts like a standard
e
descriptor, but exponents are limited to a subset ...,-3,0,+3,+6,...,
corresponding to standard SI prefixes milli-, kilo-, mega-, and so on.
There will be an appropriate movement in the decimal point.
If an exponent greater than 999 is required, an optional e
descriptor for
the exponent itself is available:
print "(e14.3e4)", avar ! Use at least 4 digits in exponent
Note that an integer descriptor is allowed to be of the form iw.d, in which case leading zeros are added to pad to the width w, if required.
One can use a string literal as part of the edit descriptor itself, e.g.,
real :: avar = 4.0
print "('This is the result: ', e14.7)", avar
There are a number of these. They do not correspond to an item in the
io-list, but alter the appearance of the output. The most common is
x
which introduces a space (or blank). E.g.,
print "(i12,x,e12.6)", ivar, avar
This will ensure at least on space between the two items in the list.
If a leading plus sign is wanted, use the sp
control, e.g.:
print "(sp,e12.6)", abs(avar)
Compile and run the accompanying example1.f90
, which provides some specimen
formats. Some of the format specifiers have not allowed a large enough
width. What's the result? What's the solution?
For a larger number of items of the same type, it can be useful to specify a repeat count. Simply prefix a literal integer count to the format, e.g.:
real, dimension(4) :: a = [ 1.0, 2.0, 3.0, 4.0]
print "(4(2x,e14.7))", a(1:4)
In this way, more complex formats can be constructed.
Variables of complex
type are treated as two real
values for the
purposes of a format specifier. The real and imaginary part do not have to
have the same edit descriptor.
A "safe" (portable) minimum width for an l
(logical) edit descriptor is
7 characters to accommodate ".false.
", although some implementations
may only produce an F
.
If no new line at all is wanted, one can use the advance = no
argument
to write()
, e.g.:
write (*, "('Input an integer: ')", advance = 'no')
read (*, *) ivar
Non-advancing output must not use list-directed I/O.
Formally, a format specifier may also be a statement label. For example,
write (unit = myunit, fmt = 100) ia, ib, c
100 format(i3, i3, f14.7)
Here, the statement at the line with label 100
is the format
keyword,
which may be used to define a format specifier.
This is very common in older forms of Fortran. However, as the reference
and the statement can become widely separated, this can be troublesome.
However, statement labels are useful in other contexts, as we will see
when we come to error handling.
Using the example program, check what happens if there is a mismatch between the format specifier and the data type in the i/o list. Choose an example, and run the program.
What happens if the number of items in the io-list is larger than the number of edit descriptors?
Additional exercise. The b
edit descriptor can be used to convert integer
values
to their binary representation. For a 32-bit integer type, the recommended
descriptor would be b32.32
to ensure the leading zeros are visible. You
may wish to try this out.