Chapter 7 — Key Takeaways (I/O and Formatted Output)
A one-page reference for reading data in, writing results out, and controlling exactly how they look.
The core edit descriptors
| Descriptor | What it does | Example → output |
|---|---|---|
iw |
integer, right-justified in width w |
i6 of 42 → ····42 |
i0 |
integer, minimal width | i0 of 42 → 42 |
iw.m |
integer, ≥ m digits (zero-padded) |
i5.3 of 7 → ··007 |
fw.d |
fixed-point real, d decimals, width w |
f8.2 of 3.14159 → ····3.14 |
ew.d |
scientific, 0.xxxxE±ee form |
e12.4 of 1e-4 → ··0.1000E-03 |
esw.d |
scientific, mantissa 1–9 (physicist's form) | es12.4 of 1e-4 → ··1.0000E-04 |
a / aw |
character string (as-is / in width w) |
a of 'hi' → hi |
nx |
insert n blanks (consumes no value) |
3x → ··· |
/ |
end this record, start a new line | — |
*(...) |
unlimited repeat (F2008): apply to all items | '(*(f8.2))' writes a whole row |
(dots mark spaces.) The grammar to memorize: w.d = field width . digits after the point.
Two rules that bite
- Overflow → asterisks. A value too wide for its field prints as
******, never truncated. Widen the field or switch toes. A column of asterisks means "too narrow," not "bad data." - Formatted vs list-directed leading space.
print '(a)', xwrites no leading space;print *, xwrites one leading blank (the carriage-control ghost). Use formatted when the first column matters.
List-directed vs formatted — which to use
Use list-directed (*) |
Use a format '(...)' |
|---|---|
| Quick debug dumps, throwaway messages | Anything a human or program will read |
| Reading a few values you typed | Tables, aligned columns, files with a fixed layout |
| You do not care about spacing/precision | You need exact width, decimals, alignment |
The file toolkit
open(newunit=u, file='out.dat', status='replace', action='write', iostat=ios, iomsg=msg)
write(u, '(f8.2)') x ! or write(u, *) x (list-directed)
read(u, *) y ! read one value list-directed
close(u)
inquire(file='out.dat', exist=ok) ! ask before you open
newunit=u— runtime picks a free unit; never hard-codeopen(17, ...).status=—'replace'(create/overwrite),'old'(must exist),'new'(must not),'scratch'(temporary, auto-deleted).action=—'read','write','readwrite'.- Sequential (default) = records in order. Direct =
access='direct', recl=N, address byrec=k;reclunits are processor-defined (bytes in gfortran — useinquire(iolength=n)for portability).
namelist — key/value configuration
namelist /config/ nx, ny, alpha, dt, n_steps
read(u, nml=config) ! parses a &config nx=100, alpha=1e-4 / block, by name
write(u, nml=config) ! dumps the group back out (provenance / restart)
Names match case-insensitively, in any order; omitted names keep their prior (default) value.
The file group opens with &config and closes with /. Always read with iostat so a typo'd key or group
name fails loudly.
Text vs binary — the decision that matters at scale
Text (f, es) |
Unformatted | Stream | |
|---|---|---|---|
| Human-readable | yes | no | no |
| Exact (no precision loss) | no | yes | yes |
| Fast / compact in bulk | no | yes | yes |
| Record markers | n/a | yes (Fortran-only) | none (C/Python-friendly) |
| Self-describing | no | no | no → use HDF5/NetCDF (Ch. 25) |
open(newunit=u, file='f.bin', form='unformatted', access='stream', status='replace', action='write')
write(u) field ! whole array, exact bytes, one statement
Rule: text for humans and small data; binary for machines and big data.
Error handling: iostat / iomsg
read(u, *, iostat=ios) x
if (ios == iostat_end) exit ! iostat_end from iso_fortran_env — NOT -1
if (ios /= 0) then ... ! positive = real error; iomsg = the message
iostat: 0 = success, negative = end-of-file/record (iostat_end, iostat_eor), positive =
error. iomsg fills a string with the description. Guard every open; loop reads until iostat_end.
Pitfalls
- Hard-coding
-1for end-of-file → useiostat_end. - Reading a string with spaces list-directed (stops at the first blank) → read with
'(a)'. - Field too narrow → asterisks (widen it).
- Direct-access
reclin the wrong units → non-portable file. - Namelist group-name/key typo with no
iostat→ silently ignored.
Compile flags introduced
None. I/O needs no special flags: gfortran -std=f2018 -Wall -O2 file.f90 handles all of it.
Numbers & rules worth memorizing
f8.2→ 8 wide, 2 decimals, right-justified. Predict any line to the column fromw.d.- A
real(dp)is 8 bytes as binary; ~24 bytes as full-precision text — binary is ~3× smaller and ~100× faster to write in bulk (illustrative orders of magnitude). newunitfor units;iostat_endfor EOF;&group … /for namelist.
Heat-solver piece added this chapter
The solver stops hard-coding its parameters: it reads nx, ny, alpha, dt, n_steps from a namelist file
(heat.nml) and writes the field to a text file via write_field(field, filename) (assumed-shape
array, deferred-length filename, '(*(f8.2))' per row). These become the heat_io module in
Chapter 8; write_field grows a
write_vtk sibling in Chapter 26.