Formats and edit descriptors
Overview
Teaching: 10 min
Exercises: 10 minQuestions
How do I read input from the keyboard to my program?
How can I control the format of input and output of various types?
Objectives
Use the
read
statement analogously towrite
to obtain input from the keyboard.Learn how to combine edit descriptors to create a format specifier.
Use format specifiers to control the appearance of output of the various types we have encountered.
Input and output
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 the variables which will store the incoming values.
The standard input unit (input_unit
from iso_fortran_env
in the above)
is typically the keyboard (or “screen”) for interactive use.
Format specifier
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 a format specifier, either string or *
, 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.
Edit descriptors
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
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.
Character string edit descriptors
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
Control edit descriptors
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 one 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)
Exercise (2 minutes)
Exercise name
Compile and run the accompanying example1.f90 program, which provides some specimen formats. Some of the format specifiers have not been allowed a large enough width. What’s the result? What’s the solution?
Solution
Using gfortran produces the following output:
Format i10: 40 Format i10.10: 0000000040 Format l10: F Format f10.3: 40.000 Format e10.3: 0.400E+02 Format en10.3: 40.000E+00 Format a10: Hello Format i1: * Format f10.3: ********** Format e10.3: 0.180+309 Others: Format sp,e12.3: +0.180+309 Format e12.3e4: 0.180E+0309
Where the format specifiers are too narrow, the output is reduced to asterisks
*
in the space that has been provided. To fix this, the specifiers need to be widened. For example, the integer specifier on line 25 isi1
, clearly not enough to print-40.
Making iti3
allows it to output.The next failed output uses
f10.3
. It’s trying to print ahuge()
number, where it was used to store the largest possible number of the same type as the originalavar
. As areal64
, that’s approximately1.80E+308
. The format specifier provides a width of 10 characters to print a number with 309 digits (although we only have precision in the first 15 or so). Thef
format specifier is very definitely unsuitable; one of thee
types such ases24.17
is much more appropriate.
Repeat counts
For a larger number of items of the same type, it can be useful to specify a repeat count. Simply place the items to be repeated within parentheses and 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)
Multiple subformats are comma-separated, much like the individual items, and subformats can also be nested. In this way, more complex formats can be constructed.
Complex variables
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.
Logical variables
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
.
New lines
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.
Statement labels
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.
Exercise (5 minutes)
Experimenting with formats
Using the example1.f90 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 beb32.32
to ensure the leading zeros are visible. You may wish to try this out.Solution
Using the wrong type variable with a format specifier should still allow the code to compile, but you will receive a runtime error when it is encountered.
Any extra items in the io-list above what the format provides will not be printed.
Key Points
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.