Internal Files, Namelist and Non-Advancing I/O

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Fortran 2023 (ISO/IEC 1539-1:2023, J3/23-007r1) and was checked on 2026-10-19. Every example was compiled with gfortran -std=f2023 -Wall -Wextra -pedantic -fimplicit-none -fcheck=all (GNU Fortran 16.2) and run, and the examples were also built and run with LLVM Flang 23; the output shown is real output, and where the two compilers print different things both are shown. Examples that are deliberately wrong, or that expose a difference between the compilers, are labelled as such and show the real diagnostics. The rules are in these places in the standard: internal files are 12.4, the control information list of a data transfer statement is 12.6.2 (advance= is 12.6.2.4, size= is 12.6.2.16), advancing and nonadvancing transfer is 12.3.4.2, the error, end-of-record and end-of-file conditions with iostat=, iomsg=, err=, end= and eor= are 12.11, list-directed formatting is 13.10, namelist formatting is 13.11 and the namelist statement is 8.9. Annex C.8.2 has the standard’s own notes on nonadvancing transfer.

Most of Fortran’s input and output is formatted: a format specification says how each value is edited into characters. This page covers four ways of doing formatted transfer without writing a format for every item, or without a file at all:

Tool What it does Typical use in the running example, heatflow

Internal file

a read or write whose unit is a character variable instead of a file connected to a unit

building a label such as nx=41, converting a command-line text to a number

List-directed I/O

the value types, not a format, drive the editing; values are separated by blanks, commas or slashes

reading a few numbers from a string; quick print * output while debugging

Namelist I/O

named values, in any order, with defaults kept for what is not mentioned

the configuration file heatflow.nml

Non-advancing I/O

a record is read or written by several statements, and the length of an input record is found out while reading

prompts, a CSV row built item by item, reading a line of unknown length

The format side (the edit descriptors i0, f8.3, es12.4, g0, a, /, : and the rest) is in Format Specifications and Edit Descriptors, units, open and file connection are in Input/Output: Units and Files, user-defined derived-type I/O and asynchronous transfer are in Derived-Type and Asynchronous I/O, and the string intrinsics (trim, adjustl, index, scan, F2023 split and tokenize) are in Characters and Strings. This page uses the namelist file of the running example, which is read by read_config in the module heatflow_config; the module is described in Modules and Submodules.

Internal files

An internal file "provides a means of transferring and converting data from internal storage to internal storage" (12.4). The unit of the data transfer statement is a character variable instead of a unit number, and no open, close or inquire is involved. Writing to the variable converts values to text; reading from it converts text to values, with the same editing rules as a file.

The rules of 12.4

Rule Detail

The file is a character variable

of default, ASCII or ISO 10646 kind, and not an array section with a vector subscript. A character constant or other expression is not a valid unit (see below)

A scalar is one record

its length is the length of the variable; a character array is a sequence of records, one per element, in array element order, all of the same length

Writing pads with blanks

if fewer characters are written than the record holds, the rest is filled with blanks; writing more than the record holds is not allowed (see the overrun section)

Always positioned at the first record

every data transfer to an internal file starts at the beginning of the first record (except child data transfers, see the derived-type I/O page)

Sequential formatted access only

only read and write with a format, list-directed or namelist formatting. An internal file shall not be the unit of open, close or inquire, and the positioning statements rewind, backspace and endfile take a file unit number, so they cannot name one (12.8.1)

Reading needs defined text

a record shall be read only if it is defined, so read a variable that has been assigned

Connection modes take their defaults

blank mode, pad mode, decimal mode and the rest behave as for a unit opened without those keywords (12.4)

An internal file needs a fmt= or an nml= (C1221). The specifiers that make sense without a file are allowed: iostat=, iomsg=, err=, end=, decimal=, blank=, pad=, round=, sign= and delim= (some only for input or only for output, C1212 and C1213). advance= is not allowed with an internal file (C1223), and neither are rec= and pos= (C1220) or asynchronous='yes' (C1226).

Converting values to text and back

The idiom is: write into a buffer that is longer than any value, then use len_trim or trim; read from the buffer or from any character variable or substring. A substring is a valid internal file, which is useful for reading a field out of a longer record.

program internal_write_read
  use, intrinsic :: iso_fortran_env, only: int32, real64
  implicit none
  character(len=40) :: line
  character(len=8) :: field
  integer(int32) :: nx
  real(real64) :: alpha
  integer :: stat

  ! Internal write: the variable is the "file", the record is blank padded.
  write (line, '(a, i0, a, f5.2)') 'nx=', 41, ' alpha=', 1.0_real64
  print '(3a)', '[', line, ']'
  print '(2(a, i0))', 'len=', len(line), ' len_trim=', len_trim(line)

  ! A substring is a legal internal file too.
  field = '   41'
  read (field, *, iostat=stat) nx
  print '(a, i0, a, i0)', 'nx=', nx, ' stat=', stat

  ! Internal read with an explicit format; blanks are not zeros by default.
  read (line(14:18), '(f5.2)', iostat=stat) alpha
  print '(a, f5.2, a, i0)', 'alpha=', alpha, ' stat=', stat
end program internal_write_read

Both compilers print the same:

[nx=41 alpha= 1.00                       ]
len=40 len_trim=17
nx=41 stat=0
alpha= 1.00 stat=0

Notes on the example: the record is the whole 40-character variable, so the text ends in blanks (len_trim is 17, the length of nx=41 alpha= 1.00); the field width of f5.2 gives the leading blank in alpha= 1.00, so i0 and f0.d are the usual choice when the text must not be padded; and read (field, *) is a list-directed read, which ignores the leading blanks of field.

The same technique converts strings to numbers and back inside a generic to_string, as shown in Characters and Strings.

Arrays of records

A character array is a file of as many records as it has elements. A write that has more items than the format has descriptors starts a new record by format reversion (the format is reused from its start, in a new record), and a slash edit descriptor does the same. A list-directed read continues into the next record when it needs more values, because the end of a record counts as a blank.

program array_records
  use, intrinsic :: iso_fortran_env, only: int32
  implicit none
  character(len=12) :: rec(3)
  integer(int32) :: a, b, c
  integer :: i

  ! Format reversion starts a new record: three items, one i0 item per record.
  write (rec, '(i0)') 10, 20, 30
  do i = 1, size(rec)
    print '(a, i0, 3a)', 'record ', i, ' [', rec(i), ']'
  end do

  ! A list-directed read continues into the next record when it needs more values.
  read (rec, *) a, b, c
  print '(a, 3(1x, i0))', 'read back:', a, b, c

  ! Two items per record: the format has two items, so reversion starts a new record after each pair.
  rec = ''
  write (rec, '(i0, 1x, i0)') 1, 2, 3, 4
  do i = 1, size(rec)
    print '(a, i0, 3a)', 'record ', i, ' [', rec(i), ']'
  end do
end program array_records

Both compilers print the same:

record 1 [10          ]
record 2 [20          ]
record 3 [30          ]
read back: 10 20 30
record 1 [1 2         ]
record 2 [3 4         ]
record 3 [            ]

The unused third record of the second write is blank because the program assigned rec = '' first; a record that was not written is not defined, so a program should not read it.

Overruns

An internal write shall not put more characters into a record than the record can hold. The standard says "shall", so the program is not conforming; what happens is the processor’s choice, and the two compilers choose differently. With iostat= both report the condition and do not stop; without it both terminate the program.

program overrun
  implicit none
  character(len=6) :: buffer
  integer :: stat
  character(len=80) :: msg

  write (buffer, '(a)', iostat=stat, iomsg=msg) 'too long for it'
  print '(a, i0)', 'stat=', stat
  print '(3a)', 'msg=[', trim(msg), ']'
end program overrun

GNU Fortran 16.2, then LLVM Flang 23:

stat=-2
msg=[End of record]
stat=1002
msg=[Excessive output to fixed-size record]

GNU Fortran reports the overrun as an end-of-record condition: the status is negative (iostat_eor is -2 on both compilers) and the message is End of record. LLVM Flang reports a positive error status (1002; do not rely on the number) with the message Excessive output to fixed-size record. The contents of the variable after the failed write are unspecified, so the portable test is "was the status non-zero", and the safe code makes the buffer large enough, or measures first. Without iostat= the statement terminates the program. For the three-line program 03b_overrun_fatal (a bare write (buffer, '(a)') 'too long for it') the output is, from GNU Fortran 16.2 (the backtrace is shortened) and then from LLVM Flang 23:

At line 5 of file 03b_overrun_fatal.f90
Fortran runtime error: End of record

Error termination. Backtrace:
fatal Fortran runtime error(/work/03b_overrun_fatal.f90:5): Excessive output to fixed-size record
Aborted

The unit must be a variable

A character constant cannot be an internal file. It is tempting to write read ('42', *) n, but the unit has to be a variable, because the unit of an input/output statement is either a unit number, *, or a variable. Assign the text to a variable first. Both compilers reject the constant at compile time:

program nonconst_unit
  implicit none
  integer :: n

  read ('42', *) n
  print '(i0)', n
end program nonconst_unit

GNU Fortran 16.2:

16_nonconst_unit.f90:5:8:

    5 |   read ('42', *) n
      |        1
Error: UNIT specification at (1) must be an INTEGER expression or a CHARACTER variable

LLVM Flang 23:

error: Semantic errors in 16_nonconst_unit.f90
16_nonconst_unit.f90:5:9: error: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(KIND=1,LEN=2_8)
    read ('42', *) n
          ^^^^

Deferred-length internal files (Fortran 2023)

Before Fortran 2023 the character variable of an internal write had to be long enough, which is why the idioms above use a generous fixed buffer. Fortran 2023 lets the unit of a write be a deferred-length allocatable scalar: it is assigned the characters written by intrinsic assignment, "allocating or reallocating to have length equal to the number of characters written if necessary" (12.4), and the same release allows a deferred-length allocatable iomsg= variable (12.11.6). Both are listed in the Introduction of the standard under "Input/output". The example below uses it:

! Fortran 2023: a deferred-length allocatable scalar internal file in WRITE is allocated
! to the length of the record written. Not implemented by GNU Fortran 16.2 or LLVM Flang 23.
program internal_write_f2023
  implicit none
  character(len=:), allocatable :: line

  write (line, '(a, i0, a, f6.2)') 'nx=', 41, ' alpha=', 1.0
  print '(a, i0, 3a)', 'len=', len(line), ' [', line, ']'
end program internal_write_f2023

Neither GNU Fortran 16.2 nor LLVM Flang 23 implements it. The variable stays unallocated and the compiler reports an end-of-file error (GNU Fortran, after a -Wmaybe-uninitialized warning) or an overrun (Flang):

15_internal_write_f2023.f90:7:60:

    7 |   write (line, '(a, i0, a, f6.2)') 'nx=', 41, ' alpha=', 1.0
      |                                                            ^
Warning: '.line' may be used uninitialized [-Wmaybe-uninitialized]
At line 7 of file 15_internal_write_f2023.f90
Fortran runtime error: End of file
fatal Fortran runtime error(/work/15_internal_write_f2023.f90:7): Internal write overran available records
Aborted

Until the compilers catch up, write into a fixed-length buffer and assign trim(buffer) to the allocatable string, as the convert module in Characters and Strings does. That page also shows the same limitation for iomsg= and errmsg=.

List-directed I/O

In list-directed I/O the format is *: print *, x, write (unit, *) x or read (unit, *) x. The editing is chosen by the type of each item instead of by an edit descriptor, and the values in the record are free field: they are separated by blanks, commas (semicolons when the decimal mode is comma) or slashes (13.10.2).

List-directed input

The rules for input (13.10.2 and 13.10.3) are short, and every one of them is a source of surprises:

Situation Rule

Separators

a comma, a slash, or one or more blanks between two nonblank values; consecutive blanks count as one; the end of a record counts as a blank, so values can continue on the next record

Values

a literal constant of the kind of the item (no kind suffix), optionally signed for numbers, in any form acceptable to the corresponding edit descriptor: for a real 1, 1. and 1.5e3, for a logical T or .true., for a complex (re,im); blanks are never zeros

Character values

delimited by apostrophes or quotes, in which case they may contain blanks, commas and slashes. An undelimited value (it may not start with a quote or with digits followed by an asterisk) ends at the first blank, comma, slash or end of record, and quotes inside it are not doubled

Null value

two consecutive commas (or a comma at the start of the list) leave the corresponding item unchanged; this includes an item that is undefined

Repeat counts

r*c is r copies of the constant c, and r* is r null values; no blanks are allowed inside either form

The slash

a slash ends the input statement; the remaining items of the list keep their previous values, as if null values had been supplied

Too few values

input continues on the next record; if the file or string runs out before the list is satisfied, an end-of-file condition occurs (12.11.3), and the items become undefined

Too many values

the extra values are ignored: the rest of the record is skipped by an advancing read

Invalid value

an error condition; iostat= is positive, and the standard makes the input items of the statement undefined (12.11.2)

program list_directed_input
  use, intrinsic :: iso_fortran_env, only: int32, real64
  implicit none
  character(len=48) :: text
  character(len=2) :: lines(2)
  integer(int32) :: n, m
  real(real64) :: x, y
  logical :: flag
  complex(real64) :: z
  character(len=12) :: word, phrase
  integer :: stat

  ! Blanks and commas separate values; a quoted string may contain both.
  text = '7  8.5, T  (1.0,2.0)  abc  "two words, one"'
  read (text, *) n, x, flag, z, word, phrase
  print '(a, i0, a, f0.2, a, l1)', 'n=', n, ' x=', x, ' flag=', flag
  print '(a, 2(f0.1, 1x))', 'z=', z
  print '(3a)', 'word=[', word, ']'
  print '(3a)', 'phrase=[', phrase, ']'

  ! Null values keep the previous value of the item.
  n = -1; m = -1; x = -1.0_real64; y = -1.0_real64
  text = '5, , 2.5, ,'
  read (text, *) n, m, x, y
  print '(a, 2(1x, i0), 2(1x, f0.1))', 'null values:', n, m, x, y

  ! r*c repeats a value, r* repeats a null value; the slash ends the statement.
  n = -1; m = -1; x = -1.0_real64; y = -1.0_real64
  text = '2*3 / 9.0 9.0'
  read (text, *) n, m, x, y
  print '(a, 2(1x, i0), 2(1x, f0.1))', 'repeat and slash:', n, m, x, y

  ! The end of a record is a separator too, so a list continues on the next record.
  lines = [character(len=2) :: '1', '2']
  read (lines, *) n, m
  print '(a, 2(1x, i0))', 'two records:', n, m

  ! An invalid value is an error condition (iostat > 0); running out of records is end-of-file.
  text = '12x'
  read (text, *, iostat=stat) n
  print '(a, l1)', 'bad integer is an error: ', stat > 0
  text = ''
  read (text, *, iostat=stat) n
  print '(a, l1)', 'empty record is end-of-file: ', stat < 0
end program list_directed_input

Both compilers print the same:

n=7 x=8.50 flag=T
z=1.0 2.0
word=[abc         ]
phrase=[two words, o]
null values: 5 -1 2.5 -1.0
repeat and slash: 3 3 -1.0 -1.0
two records: 1 2
bad integer is an error: T
empty record is end-of-file: T

Things worth noticing in the output: the string "two words, one" was truncated to 12 characters because phrase has length 12 (assignment rules of an intrinsic assignment, not an error); the null values left m and y at -1; the repeat count 2*3 filled the two integers; and the slash stopped the read before 9.0 9.0, so the reals kept their old values. A blank internal file gives an end-of-file condition, not an error, because the list needs a value and there is no next record, which is why reading a blank string with iostat= gives a negative status.

A list-directed read is the shortest way to parse a number in a string, but it is lenient: read (text, *) x is happy with '0.25 trailing' and with '0.25,1', and an explicit format with iostat= is stricter. Both are covered in Characters and Strings.

List-directed output

The standard describes the form of list-directed output, not its layout (13.10.4): the values are separated by one or more blanks (or by a comma or semicolon, optionally surrounded by blanks), every record starts with a blank, integers are written as if with Iw, reals as if with 0PFw.d or 1PEw.d Ee depending on their magnitude within a processor-dependent range, logicals as T or F, complex values in parentheses, and character values without delimiters unless the delim= mode says otherwise. The widths and digits are "reasonable processor-dependent values", so no two compilers agree, and a program that parses its own print * output depends on one compiler. Two adjacent undelimited character values are not separated at all, which is why print *, 'text', 'more' prints textmore.

program list_directed_output
  use, intrinsic :: iso_fortran_env, only: int32, real32, real64
  implicit none
  character(len=80) :: line
  integer :: i

  ! The standard fixes only the form of the values, not the widths or digits (13.10.4).
  print *, 42_int32, -7_int32
  print *, 0.5_real32, 0.5_real64
  print *, 1.0_real64 / 3.0_real64
  print *, 100.0_real64, 1.0e-5_real64, 1.0e10_real64
  print *, 0.0_real64
  print *, .true., .false.
  print *, (1.5_real64, -2.0_real64)
  print *, 'text', 'more'

  ! Repeated values: the processor may print r*c.
  print *, 1, 1, 1, 2
  print *, [(0.0_real64, i = 1, 4)]

  ! Every record starts with a blank; internal output shows it.
  write (line, *) 'x=', 3
  print '(3a, i0)', '[', line(1:12), '] len_trim=', len_trim(line)

  ! delim= changes how character values are written: a quote delimiter doubles inner quotes.
  write (line, *, delim='quote') 'say "hi"', 'ok'
  print '(3a)', '[', trim(line), ']'
  write (line, *, delim='apostrophe') 'it''s', 'ok'
  print '(3a)', '[', trim(line), ']'
end program list_directed_output

GNU Fortran 16.2, then LLVM Flang 23:

          42          -7
  0.500000000      0.50000000000000000
  0.33333333333333331
   100.00000000000000        1.0000000000000001E-005   10000000000.000000
   0.0000000000000000
 T F
              (1.5000000000000000,-2.0000000000000000)
 textmore
           1           1           1           2
   0.0000000000000000        0.0000000000000000        0.0000000000000000        0.0000000000000000
[ x=         ] len_trim=15
[ "say ""hi""" "ok"]
[ 'it''s' 'ok']
 42 -7
 .5 .5
 .3333333333333333
 100. 1.E-05 10000000000.
 0.
 T F
 (1.5,-2.)
 textmore
 1 1 1 2
 0. 0. 0. 0.
[ x= 3       ] len_trim=5
[ "say ""hi""" "ok"]
[ 'it''s' 'ok']

The differences are large. GNU Fortran prints every integer in a field of fixed width and every real with all the digits of its kind in a wide field; Flang prints short forms with a single blank between values and omits leading zeros (.5) and trailing zeros (100., 0.). Neither is wrong. The consequences:

  • Use list-directed output for diagnostics only. For files, reports and anything another program reads, use an explicit format (es12.5, f8.3, g0, i0).

  • The first column is a blank. Each record starts with a blank (13.10.4), as the internal file shows ([ x=...); the standard does not give the first column any special meaning (the compatibility notes in 4.3.6 say that earlier standards did, as printer carriage control).

  • Repeat counts are optional. The processor "has the option" of producing r*c for identical successive values; neither compiler does here (print *, 1, 1, 1, 2 prints four values), but a reader of list-directed data must accept them.

  • The output of a real zero changed in the standard. The compatibility list of the standard (4.3.6, Fortran 95) notes that the document "specifies a different output format for real zero values in list-directed and namelist output", and the two compilers show two forms (0.0000000000000000 and 0.).

  • delim= is the way to get quoted strings. With delim='quote' or delim='apostrophe', character values are delimited, are separated from the next value, and have any inner delimiter doubled, which makes the output readable by a list-directed read. The default, none, is not.

  • GNU Fortran has an environment variable for the separator. GFORTRAN_LIST_SEPARATOR sets the separator of list-directed output (any number of spaces and at most one comma; the default is a single space), but it is a runtime switch of one compiler, not part of the language. The standard way is the decimal= mode (decimal='comma' makes the separator a semicolon).

Namelist I/O

A namelist group gives a name to a list of variables. A read or write that names the group transfers all of them as name = value pairs, so the data are self-describing, order does not matter, variables can be left out, and the file can carry comments. That is exactly what a configuration file needs, and it is why heatflow reads heatflow.nml:

&heatflow
  nx = 21
  ny = 21
  lx = 1.0
  ly = 1.0
  alpha = 1.0
  dt = 5.0e-5
  hot_temperature = 100.0
  boundary_value = 0.0
  max_steps = 2000
  tolerance = 1.0e-8
  bc = 1
  solver = 'ftcs'
  output = 'heatflow.csv'
/

The namelist statement (8.9) declares the group; it follows the declarations of the variables it names. In heatflow_config the group is local to read_config, which copies the defaults of config_t into local variables, reads the group from the file, and copies the result back only on success, so an error leaves the caller’s configuration untouched:

    integer(ip) :: nx, ny, max_steps
    integer :: bc, unit
    real(dp) :: lx, ly, alpha, dt, hot_temperature, boundary_value, tolerance
    character(len=16) :: solver
    character(len=64) :: output
    namelist /heatflow/ nx, ny, lx, ly, alpha, dt, hot_temperature, &
      boundary_value, max_steps, tolerance, bc, solver, output
    nx = cfg%nx; ny = cfg%ny; lx = cfg%lx; ly = cfg%ly
    ! ... the other defaults are copied the same way ...
    msg = ''
    open (newunit=unit, file=file, status='old', action='read', iostat=stat, iomsg=msg)
    if (stat /= 0) return
    read (unit, nml=heatflow, iostat=stat, iomsg=msg)
    close (unit)
    if (stat /= 0) return

A complete program that uses it, with the module from the running example (compiled together with heatflow_kinds, heatflow_constants, heatflow_boundary and heatflow_config):

program read_config_demo
  use heatflow_boundary, only: boundary_name
  use heatflow_config, only: config_t, read_config
  implicit none
  type(config_t) :: cfg
  integer :: stat
  character(len=120) :: msg

  call read_config('heatflow.nml', cfg, stat, msg)
  print '(a, i0, 3a)', 'stat=', stat, ' [', trim(msg), ']'
  print '(a, i0, a, i0)', 'grid ', cfg%nx, ' x ', cfg%ny
  print '(a, f0.1, a, es9.2)', 'hot=', cfg%hot_temperature, ' dt=', cfg%dt
  print '(3a, i0)', 'solver=', trim(cfg%solver), ' steps=', cfg%max_steps
  print '(2a)', 'boundary=', boundary_name(cfg%bc)

  call read_config('missing.nml', cfg, stat, msg)
  print '(a, l1, 3a)', 'missing file: stat/=0 is ', stat /= 0, ' [', trim(msg), ']'
end program read_config_demo

GNU Fortran 16.2, then LLVM Flang 23. The only difference is the text of the iomsg= message for the missing file:

stat=0 []
grid 21 x 21
hot=100.0 dt= 5.00E-05
solver=ftcs steps=2000
boundary=dirichlet
missing file: stat/=0 is T [Cannot open file 'missing.nml': No such file or directory]
stat=0 []
grid 21 x 21
hot=100.0 dt= 5.00E-05
solver=ftcs steps=2000
boundary=dirichlet
missing file: stat/=0 is T [No such file or directory]

The bc variable is a plain integer in the namelist because the standard forbids enumeration types as namelist group objects (constraint C8109 of 8.9); read_config converts it to the bc_kind integer of the enumerators afterwards.

The namelist statement

Rule Detail

Syntax

namelist /group/ object-list [[,] /group/ object-list] ...; a group name can appear in several statements of a scoping unit, and the later lists continue the earlier ones

Group objects

variable names that are accessible by use or host association or whose type, kind and rank are declared before the statement; they shall not be assumed-size arrays (C8108), nor of enumeration type, nor have a direct component of enumeration type (C8109)

Use association

the group name in a namelist statement shall not be a name accessed by use association (C8107), so a module declares the group once and other units use it (see below)

Several groups

one variable can belong to more than one group

Output order

the order of the list; a variable listed twice is written twice

A group cannot be mixed with other items

a data transfer statement with nml= has no format and no input or output list (C1217)

Namelist input

The form of the input (13.11.3): optional blanks and comments, & immediately followed by the group name, one or more blanks, zero or more name = value subsequences separated by value separators, and a slash. The group name and the object names are matched without regard to case.

Situation Rule

Order and omission

subsequences can come in any order, any of them can be missing, and one can appear more than once; what is not mentioned keeps its value

Designators

a name, an array element or section with literal integer subscripts, a substring, or a component of a derived type (grid%nx); no vector subscripts, no zero-sized sections

Arrays

probe = 1, 2, 3 fills in array element order; fewer values than elements leaves the rest unchanged; more values than elements is an error

Scalars

at most one value after the equals

Values and separators

as in list-directed input: blanks or commas separate, r*c repeats, character values are delimited by apostrophes or quotes (the delim= mode of the file is ignored on input)

Null values

name = , 5 or name = followed by a separator: the item keeps its value

Comments

a ! after a value separator or in the first nonblank position of a record starts a comment up to the end of the record; a slash inside a comment does not end the group; comments are not allowed in stream input

The slash

ends the namelist input; anything after it, on the same record or later, is not read

Real and complex

a real value is a numeric input field "assumed to have no fractional digits unless a decimal symbol appears", so nx = 2 into a real is 2.0; a complex value is (re,im)

The following program reads a namelist from an array of character records (a namelist read from an internal file is legal, because an internal file must have a format or a namelist group, C1221), then writes the group to the standard output and to another internal file.

program namelist_forms
  use, intrinsic :: iso_fortran_env, only: int32, real64
  implicit none
  integer(int32) :: nx = 21, ny = 21
  real(real64) :: alpha = 1.0_real64
  real(real64) :: probe(4) = 0.0_real64
  logical :: verbose = .false.
  character(len=8) :: solver = 'ftcs'
  complex(real64) :: z = (0.0_real64, 0.0_real64)
  character(len=100) :: text(3)
  character(len=200) :: out(12)
  integer :: stat, i
  character(len=100) :: msg
  namelist /run/ nx, ny, alpha, probe, verbose, solver, z

  ! Input: any order, case-insensitive, partial, array sections, a repeat count, a comment and a slash.
  text(1) = '&RUN ny = 41, SOLVER = ''jacobi'', probe(2:3) = 2*1.5, verbose = t ! a comment'
  text(2) = '  Z = (1.0, -2.0)  alpha = 0.25'
  text(3) = '/  this text after the slash is never read'
  read (text, nml=run, iostat=stat, iomsg=msg)
  print '(a, i0)', 'stat=', stat
  print '(2(a, i0), a, f4.2, 2a)', 'nx=', nx, ' ny=', ny, ' alpha=', alpha, ' solver=', trim(solver)
  print '(a, 4(1x, f3.1))', 'probe:', probe
  print '(a, l1, a, 2f5.1)', 'verbose=', verbose, ' z=', z

  ! Output: upper-case names, the group name, one record per start of a value list, a closing slash.
  write (*, nml=run)

  ! The same output into an internal file, with quote delimiters for strings.
  out = ''
  write (out, nml=run, delim='quote')
  do i = 1, size(out)
    if (len_trim(out(i)) > 0) print '(a, i0, 3a)', 'record ', i, ' [', trim(out(i)), ']'
  end do
end program namelist_forms

GNU Fortran 16.2, then LLVM Flang 23:

stat=0
nx=21 ny=41 alpha=0.25 solver=jacobi
probe: 0.0 1.5 1.5 0.0
verbose=T z=  1.0 -2.0
&RUN
 NX=21         ,
 NY=41         ,
 ALPHA= 0.25000000000000000     ,
 PROBE=  0.0000000000000000     , 2*1.5000000000000000       ,  0.0000000000000000     ,
 VERBOSE=T,
 SOLVER="jacobi  ",
 Z=(1.0000000000000000,-2.0000000000000000),
 /
record 1 [ &RUN]
record 2 [ NX=21         ,]
record 3 [ NY=41         ,]
record 4 [ ALPHA= 0.25000000000000000     ,]
record 5 [ PROBE=  0.0000000000000000     , 2*1.5000000000000000       ,  0.0000000000000000     ,]
record 6 [ VERBOSE=T,]
record 7 [ SOLVER="jacobi  ",]
record 8 [ Z=(1.0000000000000000,-2.0000000000000000),]
record 9 [ /]
stat=0
nx=21 ny=41 alpha=0.25 solver=jacobi
probe: 0.0 1.5 1.5 0.0
verbose=T z=  1.0 -2.0
 &RUN NX= 21,NY= 41,ALPHA= .25,PROBE= 0. 1.5 1.5 0.,VERBOSE= T,SOLVER= jacobi
 ,Z= (1.,-2.)/
record 1 [ &RUN NX= 21,NY= 41,ALPHA= .25,PROBE= 0. 1.5 1.5 0.,VERBOSE= T,SOLVER= "jacobi  ",Z= (1.,-2.)/]

The read shows the input rules at work: the group name &RUN and the object names SOLVER and Z were written in upper case and matched anyway; the array section probe(2:3) = 2*1.5 used a repeat count; verbose = t was followed by a comment; alpha = 0.25 was on a second record; and the slash on the third record ended the input, so the text after it was never read. nx was not mentioned and kept 21.

Namelist output

Namelist output (13.11.4) has the form of a namelist input record: &GROUP, then NAME=value pairs in the order of the namelist statement, then a slash. The names are in upper case and no comments are produced. The values are edited as for list-directed output (13.11.4.2), so the layout is as processor dependent as in the previous section: GNU Fortran writes one object per record with wide numeric fields and a repeat count for identical neighbours (2*1.5000...), Flang writes everything on one long record with the shortest representation. (GNU Fortran starts the first record at column 1 when the unit is the standard output; the internal file shows the usual leading blank.)

Two points matter for round trips:

  • The default delim=none writes strings without delimiters, so a string with a blank, comma or slash is not read back correctly. Open the unit with delim='quote' (or apostrophe) or put delim='quote' in the write statement, as the second write of the example does. (Namelist output is "suitable for input, except for character output", 13.11.4.1.)

  • A character variable is written with its full length, blanks included ("jacobi "), and read back the same way.

Derived types and public groups

For a derived-type variable the designator in the input can be a component, grid%nx = 41, or the whole variable, in which case the values are assigned to the ultimate components in order, as if the variable were expanded into a list of scalar items (13.11.3.2). A namelist group is a name like any other: it can be declared in a module, made public, and used elsewhere without naming the variables in it.

module heatflow_settings
  use, intrinsic :: iso_fortran_env, only: int32, real64
  implicit none
  private
  public :: grid_spec_t, grid, seed, settings, apply_text

  type :: grid_spec_t
    integer(int32) :: nx = 21, ny = 21
    real(real64) :: lx = 1.0_real64, ly = 1.0_real64
  end type grid_spec_t

  type(grid_spec_t) :: grid
  integer(int32) :: seed = 42
  namelist /settings/ grid, seed

contains

  subroutine apply_text(text, stat)
    character(len=*), intent(in) :: text(:)
    integer, intent(out) :: stat

    read (text, nml=settings, iostat=stat)
    print '(a, i0)', 'seed=', seed
  end subroutine apply_text

end module heatflow_settings

program namelist_derived
  use heatflow_settings, only: grid, settings, apply_text
  implicit none
  character(len=60) :: text(1)
  integer :: stat

  ! Components are addressed with %, and the whole variable can be given as a list of components.
  text(1) = '&settings grid%nx = 41, grid%lx = 2.0, seed = 7 /'
  call apply_text(text, stat)
  print '(a, i0, a, i0, a, i0, a, f3.1)', 'stat=', stat, ' nx=', grid%nx, ' ny=', grid%ny, ' lx=', grid%lx

  text(1) = '&settings grid = 11, 12, 0.5, 0.25 /'
  call apply_text(text, stat)
  print '(a, i0, a, i0, a, i0, a, f4.2)', 'stat=', stat, ' nx=', grid%nx, ' ny=', grid%ny, ' ly=', grid%ly

  ! The group is a single name that any program unit with access to it can read or write.
  write (*, nml=settings)
end program namelist_derived

GNU Fortran 16.2, then LLVM Flang 23:

seed=7
stat=0 nx=41 ny=21 lx=2.0
seed=7
stat=0 nx=11 ny=12 ly=0.25
&SETTINGS
 GRID%NX=11         ,
 GRID%NY=12         ,
 GRID%LX= 0.50000000000000000     ,
 GRID%LY= 0.25000000000000000     ,
 SEED=7          ,
 /
seed=7
stat=0 nx=41 ny=21 lx=2.0
seed=7
stat=0 nx=11 ny=12 ly=0.25
 &SETTINGS GRID= 11 12 .5 .25,SEED= 7/

The output shows that GNU Fortran writes a derived-type group object component by component (GRID%NX=11) while Flang writes it as one list (GRID= 11 12 .5 .25); both are valid input for the other direction.

A public group with a private object (Fortran 2023)

Before Fortran 2023 a public namelist group could not contain a private variable. Fortran 2023 removes that restriction ("A PUBLIC namelist group can have a PRIVATE namelist group object", Introduction of J3/23-007r1), which lets a module expose one name that reads and writes some hidden state. The example below is the shortest way to see whether a compiler has it: neither GNU Fortran 16.2 nor LLVM Flang 23 accepts it.

module heatflow_settings
  use, intrinsic :: iso_fortran_env, only: int32, real64
  implicit none
  private
  public :: grid_spec_t, grid, settings

  type :: grid_spec_t
    integer(int32) :: nx = 21, ny = 21
    real(real64) :: lx = 1.0_real64, ly = 1.0_real64
  end type grid_spec_t

  type(grid_spec_t) :: grid
  integer(int32), private :: secret_seed = 42   ! private variable ...
  namelist /settings/ grid, secret_seed         ! ... in a public namelist group (Fortran 2023)

end module heatflow_settings

program namelist_private
  use heatflow_settings, only: grid, settings
  implicit none
  write (*, nml=settings)
end program namelist_private

GNU Fortran 16.2:

09b_namelist_private_f2023.f90:5:39:

    5 |   public :: grid_spec_t, grid, settings
      |                                       1
Error: NAMELIST object 'secret_seed' was declared PRIVATE and cannot be member of PUBLIC namelist 'settings' at (1)

LLVM Flang 23:

error: Semantic errors in 09b_namelist_private_f2023.f90
09b_namelist_private_f2023.f90:14:29: error: A PRIVATE namelist group object 'secret_seed' must not be in a PUBLIC namelist
    namelist /settings/ grid, secret_seed         ! ... in a public namelist group (Fortran 2023)
                              ^^^^^^^^^^^

Until both compilers accept it, make the variable public or keep the whole group private and expose a subroutine, as apply_text does in the previous example.

Errors, and how far the compilers agree

A namelist read can fail in several ways, and this is the area where the compilers differ most. The program below feeds one group seven inputs and reports the status class and the iomsg= text of each.

program namelist_errors
  use, intrinsic :: iso_fortran_env, only: int32, real64, iostat_end
  implicit none
  integer(int32) :: nx = 21
  real(real64) :: alpha = 1.0_real64
  character(len=60) :: text(1)
  character(len=100) :: msg
  integer :: stat
  namelist /run/ nx, alpha

  call try('&run nx = 41 /')
  call try('&run ny = 41 /')
  call try('&run nx = 4.5 /')
  call try('&run alpha = abc /')
  call try('&other nx = 1 /')
  call try('&run nx = 41')
  call try('nx = 41 /')

contains

  subroutine try(input)
    character(len=*), intent(in) :: input
    character(len=11) :: outcome

    text(1) = input
    msg = ''
    nx = 21
    alpha = 1.0_real64
    read (text, nml=run, iostat=stat, iomsg=msg)
    if (stat == 0) then
      outcome = 'ok'
    else if (stat == iostat_end) then
      outcome = 'end of file'
    else
      outcome = 'error'
    end if
    print '(6a, i0, a, f3.1)', input, ' -> ', trim(outcome), ' [', trim(msg), '] nx=', nx, ' alpha=', alpha
  end subroutine try

end program namelist_errors

GNU Fortran 16.2, then LLVM Flang 23:

&run nx = 41 / -> ok [] nx=41 alpha=1.0
&run ny = 41 / -> error [Cannot match namelist object name ny] nx=21 alpha=1.0
&run nx = 4.5 / -> error [Cannot match namelist object name .5] nx=21 alpha=1.0
&run alpha = abc / -> error [Cannot match namelist object name abc] nx=21 alpha=1.0
&other nx = 1 / -> ok [] nx=21 alpha=1.0
&run nx = 41 -> end of file [End of file] nx=41 alpha=1.0
nx = 41 / -> ok [] nx=21 alpha=1.0
&run nx = 41 / -> ok [] nx=41 alpha=1.0
&run ny = 41 / -> error ['ny' is not an item in NAMELIST group 'run'] nx=21 alpha=1.0
&run nx = 4.5 / -> ok [] nx=4 alpha=1.0
&run alpha = abc / -> error [Bad real input data at column 15 of record 1] nx=21 alpha=1.0
&other nx = 1 / -> end of file [End of file during input] nx=21 alpha=1.0
&run nx = 41 -> error [No '/' found after NAMELIST group 'run'] nx=41 alpha=1.0
nx = 41 / -> end of file [End of file during input] nx=21 alpha=1.0
Input GNU Fortran 16.2 LLVM Flang 23

&run ny = 41 / (name not in the group)

error, Cannot match namelist object name ny

error, 'ny' is not an item in NAMELIST group 'run'

&run nx = 4.5 / (real into an integer)

error, reported as an unknown name .5

accepted: nx becomes 4

&run alpha = abc / (not a number)

error, Cannot match namelist object name abc

error, Bad real input data at column 15 of record 1

&other nx = 1 / (another group name)

no error, nothing read (nx unchanged)

end of file

&run nx = 41 (no closing slash)

nx is set, then end of file

error, No '/' found after NAMELIST group 'run'

nx = 41 / (no &run)

no error, nothing read

end of file

What a program can rely on: a valid input gives status zero on both; a bad item name or bad value gives a non-zero status and, with iomsg=, a text meant for a person; and the status of an unsuccessful read must not be compared with a particular number. The rest is processor dependent or lenient, so a configuration reader should check stat /= 0, show msg, and treat an end of file the same as an error (the group name was missing or the file is empty). The numeric status values differ (-2 against 1002 for the overrun above), and the text of iomsg= is not standardised. The standard also makes the group objects undefined after an error or an end of file (12.11.2 and 12.11.3), so the values the program prints after a failed read (nx=21 above) are what the compilers happened to leave, not something to depend on; the example reset the variables before every read to make them visible.

The standard asks for an error on a real value for an integer (the value "shall be acceptable for the type of the corresponding effective item", 13.11.3.3), and that group names match, so Flang’s acceptance of 4.5 for an integer, and the compilers' handling of a group-name mismatch, are the ones to watch if a configuration file is checked strictly.

Overriding settings from the command line

Because a namelist can be read from a string, one name = value assignment from the command line can be turned into a one-line namelist and parsed by the same code that reads the file, with the same defaults and the same errors:

program cli_overrides
  use, intrinsic :: iso_fortran_env, only: int32, real64
  implicit none
  integer(int32) :: nx = 21, ny = 21, max_steps = 2000
  real(real64) :: alpha = 1.0_real64, dt = 5.0e-5_real64
  namelist /heatflow/ nx, ny, alpha, dt, max_steps
  character(len=:), allocatable :: arg, text
  integer :: i, n, stat
  character(len=100) :: msg

  do i = 1, command_argument_count()
    call get_command_argument(i, length=n)
    allocate (character(len=n) :: arg)
    call get_command_argument(i, arg)
    text = '&heatflow ' // arg // ' /'          ! a one-assignment namelist record
    read (text, nml=heatflow, iostat=stat, iomsg=msg)
    if (stat /= 0) then
      print '(5a)', 'bad argument "', arg, '": ', trim(msg)
    end if
    deallocate (arg)
  end do
  print '(2(a, i0), a, f4.2, a, es8.1, a, i0)', 'nx=', nx, ' ny=', ny, ' alpha=', alpha, ' dt=', dt, ' max_steps=', max_steps
end program cli_overrides

Run with the arguments nx=41 alpha=0.25 bogus=3 max_steps=500, GNU Fortran 16.2 then LLVM Flang 23 print:

bad argument "bogus=3": Cannot match namelist object name bogus
nx=41 ny=21 alpha=0.25 dt= 5.0E-05 max_steps=500
bad argument "bogus=3": 'bogus' is not an item in NAMELIST group 'heatflow'
nx=41 ny=21 alpha=0.25 dt= 5.0E-05 max_steps=500

(Character values need quotes, which the shell removes, so solver='jacobi' has to be quoted twice on a command line.)

Non-advancing I/O

A normal (advancing) data transfer statement reads or writes one or more whole records: after it, the file is positioned after the record. With advance='no' the statement leaves the file positioned inside the current record, so the next statement continues where the last one stopped (12.3.4.2). It is the tool for prompts, for building a record from several statements and for reading a record whose length is not known in advance.

The specifiers

Specifier Meaning and rules Clause

advance='no'

nonadvancing transfer; the value is 'yes' or 'no' without regard to case, and the default is 'yes'. Only in a formatted sequential or stream data transfer with an explicit format, so not with *, not with nml= and not with an internal file (C1223)

12.6.2.4

size=

a scalar integer variable that receives the number of characters transferred by data edit descriptors, not counting padding blanks. Input only (C1213), never in list-directed or namelist input (C1214); defined when the statement completes, or at the wait for asynchronous input

12.6.2.16

eor=label

branch taken at an end-of-record condition; needs advance= (C1224) and is a nonadvancing-input feature

12.11.4

iostat=

zero on success, positive on error, iostat_end (negative) at end of file, iostat_eor (negative) at end of record

12.11.5

pad=

'yes' (the default) pads a short record with blanks; 'no' makes a short record an error or an end-of-record. Set on open or per statement

12.5.6.14, 12.6.2.11

Annex C.1 of the standard lists "The SIZE= specifier can be used with advancing input" among the features new in Fortran 2018, so before that a compiler could demand advance='no' with size=; GNU Fortran 16.2 still does:

program constraint_errors
  implicit none
  character(len=20) :: text
  character(len=4) :: item
  integer :: got

  text = 'abcd'
  read (text, '(a4)', advance='no') item         ! advance= with an internal file
  read (*, *, size=got) item                      ! size= with list-directed input
  read (*, '(a4)', eor=10) item                   ! eor= without advance='no'
10 continue
end program constraint_errors
17_constraint_errors.f90:8:30:

    8 |   read (text, '(a4)', advance='no') item         ! advance= with an internal file
      |                              1
Error: ADVANCE tag at (1) is incompatible with internal file
17_constraint_errors.f90:9:20:

    9 |   read (*, *, size=got) item                      ! size= with list-directed input
      |                    1~~
Error: SIZE tag at (1) requires an ADVANCE tag
17_constraint_errors.f90:10:25:

   10 |   read (*, '(a4)', eor=10) item                   ! eor= without advance='no'
      |                         1
Error: EOR tag at (1) requires an ADVANCE tag

LLVM Flang 23 reports the first and third as errors (If ADVANCE appears, UNIT=internal-file must not appear and If EOR appears, ADVANCE with value 'NO' must also appear) and the second only as a portability warning (If FMT=* appears, SIZE should not appear).

Prompts, and records built item by item

On output, a nonadvancing write leaves the record open. The classic use is a prompt; the other is a record whose items are produced by a loop, where the separator must go between items and not after the last:

program nonadvancing_output
  use, intrinsic :: iso_fortran_env, only: int32, real64
  implicit none
  real(real64) :: row(5)
  integer(int32) :: j, unit
  character(len=200) :: line
  integer :: stat

  ! A prompt: the cursor stays on the line because advance='no' leaves the record open.
  write (*, '(a)', advance='no') 'Grid size nx: '
  write (*, '(a)') '41'

  ! One CSV row built item by item, with no trailing comma.
  row = [5.0_real64, 12.5_real64, 25.0_real64, 12.5_real64, 5.0_real64]
  open (newunit=unit, status='scratch', action='readwrite', iostat=stat)
  do j = 1, size(row)
    if (j > 1) write (unit, '(a)', advance='no') ','
    write (unit, '(f0.2)', advance='no') row(j)
  end do
  write (unit, '(a)') ''                 ! an advancing write completes the record
  rewind (unit)
  read (unit, '(a)') line
  print '(3a)', '[', trim(line), ']'
  close (unit)

end program nonadvancing_output

Both compilers print the same:

Grid size nx: 41
[5.00,12.50,25.00,12.50,5.00]

The loop writes , before every item but the first (the alternative, a format with the colon edit descriptor in one statement, is what write_csv in heatflow_io uses, with (*(es12.5,:,","))), and the final advancing write (unit, '(a)') '' completes the record. The same technique writes a row whose items come from different places.

Two things the standard does not promise, both in C.8.2:

  • The prompt may not be displayed. Annex C.8.2 gives the prompt as an example and then says "This document does not require that an implementation of nonadvancing input/output operate in this manner": an implementation can defer the display of a record until it is complete. A flush (output_unit) after the prompt (the flush statement, 12.9) asks for it to be shown before the program waits for input. The examples here write to a pipe, so the prompt behaviour on a terminal was not observed.

  • What happens to an open record at the end. If a nonadvancing output statement leaves a file positioned within the record and no other output statement follows before the file is closed or a backspace, endfile or rewind is executed, "the effect is as if the output statement were the corresponding advancing output statement" (12.3.4.2). On the standard output, both compilers leave the last record without a line terminator when the program ends:

program open_record_at_exit
  implicit none

  write (*, '(a)', advance='no') 'no final newline'
end program open_record_at_exit

The output is no final newline, with no line feed after it, on both GNU Fortran 16.2 and LLVM Flang 23. Finish every nonadvancing sequence with an advancing statement.

Reading a line of unknown length

An advancing read (unit, '(a)') line into a fixed-length variable cuts a long line and pads a short one, and a deferred-length variable is not allocated by a read. The portable way to read a whole record of any length is to read it in chunks with advance='no', using size= to know how many characters each chunk really contains and iostat_eor to know when the record ended:

module line_reader
  use, intrinsic :: iso_fortran_env, only: iostat_end, iostat_eor
  implicit none
  private
  public :: read_line

contains

  !> Read one record of any length into a deferred-length string.
  !> stat is 0 on success, iostat_end at end of file and positive on error.
  subroutine read_line(unit, line, stat)
    integer, intent(in) :: unit
    character(len=:), allocatable, intent(out) :: line
    integer, intent(out) :: stat
    character(len=64) :: chunk
    integer :: got

    line = ''
    do
      read (unit, '(a)', advance='no', iostat=stat, size=got) chunk
      line = line // chunk(1:got)          ! got characters were transferred, padding not counted
      if (stat == iostat_eor) then         ! end of the record: the line is complete
        stat = 0
        return
      end if
      if (stat /= 0) return                ! end of file or error
    end do
  end subroutine read_line

end module line_reader

program read_lines
  use line_reader, only: read_line
  use, intrinsic :: iso_fortran_env, only: iostat_end
  implicit none
  character(len=:), allocatable :: line
  integer :: unit, stat, n

  open (newunit=unit, status='scratch', action='readwrite')
  write (unit, '(a)') 'short'
  write (unit, '(a)') ''
  write (unit, '(a)') repeat('x', 150)
  write (unit, '(a)') 'last line'
  rewind (unit)

  n = 0
  do
    call read_line(unit, line, stat)
    if (stat == iostat_end) exit
    if (stat /= 0) stop 'read error'
    n = n + 1
    print '(a, i0, a, i0, 3a)', 'line ', n, ' has ', len(line), ' characters [', line(1:min(len(line), 10)), ']'
  end do
  close (unit)
end program read_lines

Both compilers print the same:

line 1 has 5 characters [short]
line 2 has 0 characters []
line 3 has 150 characters [xxxxxxxxxx]
line 4 has 9 characters [last line]

How it works: a nonadvancing read of an a edit descriptor of 64 characters takes up to 64 characters of the current record. If the record has more, the statement completes with status zero and the file stays in the record; if the record has fewer, the end-of-record condition occurs, iostat is iostat_eor, the chunk is padded with blanks (pad mode yes), and size= has the number of characters really read, which is what chunk(1:got) appends. The empty second line is a record of zero characters: got is 0 and the status is iostat_eor at once. End of file is a different, also negative, status (iostat_end), and the loop in the main program stops on it. The same function can be the basis of a library that reads text data files line by line, with index or split (F2023) to cut the line afterwards.

The two statuses are tested more readably with the intrinsic functions is_iostat_end and is_iostat_eor (16.9.116 and 16.9.117), which return true for any value of the processor’s end-of-file and end-of-record status:

program iostat_values
  use, intrinsic :: iso_fortran_env, only: iostat_end, iostat_eor
  implicit none
  integer :: n, stat
  character(len=4) :: text

  print '(2(a, i0))', 'iostat_end=', iostat_end, ' iostat_eor=', iostat_eor
  text = ''
  read (text, *, iostat=stat) n
  print '(a, i0, a, l1, a, l1)', 'empty record: stat=', stat, ' is_iostat_end=', is_iostat_end(stat), ' is_iostat_eor=', is_iostat_eor(stat)
  text = 'x'
  read (text, *, iostat=stat) n
  print '(a, l1, a, l1, a, l1)', 'bad value: stat>0 ', stat > 0, ' is_iostat_end=', is_iostat_end(stat), ' is_iostat_eor=', is_iostat_eor(stat)
end program iostat_values

Both compilers print the same:

iostat_end=-1 iostat_eor=-2
empty record: stat=-1 is_iostat_end=T is_iostat_eor=F
bad value: stat>0 T is_iostat_end=F is_iostat_eor=F

The eor= specifier and incomplete records

eor=label is the older way to react to the end of a record (it is not obsolescent, see below): control branches to the label, the item is padded as with iostat=, and the file is positioned after the record. Its drawbacks are the label and the branch out of the middle of a loop; iostat= with iostat_eor gives the same information without them, so it is the better default. Both are shown here, on a file whose last record has no line terminator:

program eor_and_incomplete
  use, intrinsic :: iso_fortran_env, only: iostat_end, iostat_eor
  implicit none
  character(len=8) :: chunk
  integer :: unit, got, stat

  ! A file whose last record has no line terminator (written as a stream of bytes).
  open (newunit=unit, file='eor_demo.txt', access='stream', form='unformatted', status='replace')
  write (unit) 'abc' // achar(10) // 'defgh'
  close (unit)

  open (newunit=unit, file='eor_demo.txt', status='old', action='read')

  ! eor= branches when the record is shorter than the item; size= says how many characters arrived.
  chunk = '--------'
  read (unit, '(a8)', advance='no', eor=100, size=got) chunk
  print '(a)', 'no end of record: not reached'
  stop
100 continue
  print '(a, i0, 3a)', 'eor: got=', got, ' chunk=[', chunk, '] (pad=yes filled the rest with blanks)'

  ! With iostat= the same condition is reported as iostat_eor, and the record is complete.
  chunk = '--------'
  read (unit, '(a8)', advance='no', iostat=stat, size=got) chunk
  print '(a, l1, a, i0, 3a)', 'stat==iostat_eor: ', stat == iostat_eor, ' got=', got, ' chunk=[', chunk, ']'
  print '(a, l1)', 'stat==iostat_end: ', stat == iostat_end
  close (unit, status='delete')
end program eor_and_incomplete

Both compilers print the same:

eor: got=3 chunk=[abc     ] (pad=yes filled the rest with blanks)
stat==iostat_eor: T got=5 chunk=[defgh   ]
stat==iostat_end: F

The last record defgh has no terminator, and both compilers treat it as a complete record (end of record, not end of file), which is what most programs want. The standard’s distinction between a complete and an incomplete record, which turns the end-of-record condition into an end-of-file condition, is made for stream files (12.11.1 and 12.3.3.4); these two compilers did not make it for a formatted sequential file.

Pad mode

The pad mode decides what happens when the input list needs more characters than the record has. With pad='yes' (the default for a connection) the record is logically extended with blanks, which is why reading '(a8)' from a 3-character record gives abc and five blanks. With pad='no' an advancing read shall not require more characters than the record contains (12.6.4.5.3), and a nonadvancing read gets an end-of-record condition. The compatibility clause of the standard (4.3.8) records that Fortran 77 required the number of characters the input list needs to be at most the number in the record, and that the standard now pads unless pad='no' is in effect.

program pad_no
  use, intrinsic :: iso_fortran_env, only: iostat_eor
  implicit none
  character(len=8) :: item
  integer :: unit, stat
  character(len=100) :: msg

  open (newunit=unit, status='scratch', action='readwrite', pad='no')
  write (unit, '(a)') 'abc'
  write (unit, '(a)') 'def'
  rewind (unit)

  item = 'XXXXXXXX'
  read (unit, '(a8)', iostat=stat, iomsg=msg) item         ! advancing, pad='no': the record is too short
  print '(a, l1, a, l1, 3a)', 'advancing: stat>0 ', stat > 0, ' eor ', stat == iostat_eor, ' item=[', item, ']'
  print '(3a)', 'iomsg=[', trim(msg), ']'

  item = 'XXXXXXXX'
  read (unit, '(a8)', advance='no', iostat=stat) item        ! non-advancing: end-of-record condition
  print '(a, l1, 3a)', 'non-advancing: eor ', stat == iostat_eor, ' item=[', item, ']'
  close (unit)
end program pad_no

GNU Fortran 16.2, then LLVM Flang 23:

advancing: stat>0 F eor T item=[XXXXXXXX]
iomsg=[End of record]
non-advancing: eor T item=[XXXXXXXX]
advancing: stat>0 T eor F item=[abcXXXXX]
iomsg=[Excessive input from fixed-size record]
non-advancing: eor T item=[XXXXXXXX]

The advancing read of a short record with pad='no' is a non-conforming program, and the compilers differ again: GNU Fortran reports an end-of-record condition (negative status, item unchanged), while Flang reports a positive error status and has already stored the three characters it found (abcXXXXX). The nonadvancing read behaves the same on both.

Error handling for these statements

All the statements on this page report problems through the same conditions (12.11):

Condition When How to handle it

Error

a bad value, an overrun, an unknown namelist name, a file that cannot be opened

iostat= is positive; iomsg= has a text; err=label branches

End of file

input past the last record, or an empty internal file; for namelist also a missing slash (GNU Fortran)

iostat= is iostat_end (negative); end=label branches

End of record

nonadvancing input that needs more characters than the record has (and, on GNU Fortran, an internal write that overran)

iostat= is iostat_eor (negative); eor=label branches

If a condition occurs in a statement that has no specifier for it (err= or iostat= for an error, end= or iostat= for end of file, eor= or iostat= for end of record), error termination is initiated (12.11.2, 12.11.3 and 12.11.4); with a specifier, the program continues and the status is defined. err=, end= and eor= are not in the standard’s list of obsolescent features (Annex B.3), despite what some textbooks say, but iostat= and iomsg= are the better style because they work in a do loop without labels and can be passed on. After an error the position of the file is indeterminate (12.11.2), so a loop should stop, not retry.

Where the books are outdated

The two books consulted for this section describe internal files, list-directed I/O, namelist and nonadvancing I/O as they were in Fortran 90 to 2018. The language has moved on in these places:

Area Outdated in the books, current approach

err= and end= called obsolescent

footnotes in one of the books say err= and end= are obsolescent, pointing to the appendix of the authors' own deprecated features; they are not obsolescent in the standard (Annex B lists the real obsolescent features), only less convenient than iostat= and iomsg=

Deferred-length variables

the books predate Fortran 2023 where a deferred-length allocatable iomsg= variable and a deferred-length internal-file unit of a write are allocated by the processor (Introduction, 12.4, 12.11.6); neither GNU Fortran 16.2 nor LLVM Flang 23 implements it yet, so the fixed buffer is still the portable idiom

Public namelist with private objects

a public namelist group could not contain a private variable; Fortran 2023 allows it, but no compiler checked here accepts it

size= with advancing input

described as needing advance='no'; since Fortran 2018 size= may be used with advancing input (C.1), though GNU Fortran 16.2 still insists on advance=

Reading an unknown-length line

the usual advice is a fixed-length buffer and len_trim; the non-advancing loop with size= and iostat_eor, assigned to a deferred-length string, reads any length without losing characters

Processor-dependent output

list-directed and namelist output are shown with one compiler’s layout; two compilers can differ in width, digits, leading zeros and record breaks, so output that another program parses needs an explicit format

Compiler context

the books do not say which compilers implement which behaviour; the tables on this page show where GNU Fortran 16.2 and LLVM Flang 23 differ (overrun status, namelist errors, real-to-integer input, pad mode)

Standard version provenance and compiler support

Feature Standard Compiler support checked on 2026-10-19

Internal files for formatted, list-directed and namelist transfer, iostat=, iomsg=, size=, eor=, advance=, pad=, delim=, namelist

in the standard before Fortran 2023 (the Introduction of J3/23-007r1 lists only the F2023 changes; the editions that added each are not confirmed here)

GNU Fortran 16.2 and LLVM Flang 23

is_iostat_end, is_iostat_eor, iostat_end, iostat_eor

in the standard before Fortran 2023 (16.9.116, 16.9.117, 16.10.2.16, 16.10.2.17)

both

size= with advancing input

Fortran 2018 (Annex C.1)

Flang 23 accepts it; GNU Fortran 16.2 requires advance=

Deferred-length allocatable scalar as the unit of an internal write

Fortran 2023 (12.4, Introduction)

not implemented by GNU Fortran 16.2 or LLVM Flang 23

Deferred-length allocatable iomsg= variable

Fortran 2023 (12.11.6, Introduction)

not implemented by either (see Characters and Strings)

A public namelist group with a private object

Fortran 2023 (8.9, Introduction)

rejected by GNU Fortran 16.2 and LLVM Flang 23

Different output for real zero in list-directed and namelist output

compared with Fortran 95 (4.3.6)

both print a form of zero, in different layouts

Comments, r*c repeats, null values and arrays in namelist input

as in 13.11.3

both

Not checked here, because no compiler for them is installed: Intel ifx, NAG, HPE Cray and NVIDIA HPC compilers. See History, Standards and Compilers for the per-compiler support table, and What’s New and Fortran 2028 Preview for the features added by Fortran 2023.

Pitfalls

  • A character constant is not an internal file. read ('42', *) n does not compile; assign the text to a variable.

  • Writing more than the record holds. An internal write that does not fit is an error whose status is processor dependent (end-of-record on GNU Fortran, a positive error on Flang). Size the buffer, and use iostat=.

  • Reading from a record that was never written. An unset character(len=100) :: line is undefined; assign line = '' first, or the read sees garbage.

  • A short write leaves the rest blank. trim or len_trim the buffer before using it, or the padding becomes part of the result.

  • List-directed input is lenient. read (text, *) x stops at a blank, comma or slash and accepts '0.25 trailing'; a slash silently ends the whole statement and leaves the remaining items unchanged.

  • Null values hide missing data. 5, , leaves the second item at whatever value it had; initialize items so that "not given" is detectable.

  • An undelimited string stops at the first blank. Quote strings that contain blanks, commas or slashes, in input and (with delim=) in output.

  • Never parse print * output. Its layout is the compiler’s choice. Use an explicit format, or delim= and namelist for data that a program must read back.

  • Namelist objects keep their value when absent. That is a feature (defaults), but it also means a misspelled name that is silently ignored by a lenient compiler leaves the default in force; check iostat, and print the settings that were used.

  • A namelist file must contain &group. GNU Fortran reads nothing and reports no error for a file with a different group name, Flang reports end of file; a configuration reader should treat both as an error by checking that something was read.

  • Do not compare the iostat number. Compare with zero, iostat_end or iostat_eor, or use is_iostat_end and is_iostat_eor; GNU Fortran and Flang return different positive values for the same errors.

  • Namelist output with the default delim is not valid namelist input when it contains strings with blanks or slashes; add delim='quote'.

  • Enumerations cannot be namelist objects. Use their integer kind.

  • Non-advancing output needs an advancing statement to finish. Both compilers leave the last record of the standard output without a line terminator.

  • A prompt is not guaranteed to appear before the read. Add flush (output_unit).

  • size= counts characters of the data edit descriptors, not blanks added by padding; use it, not len_trim, to find the length of a chunk.

  • pad='no' changes errors into conditions. A short advancing record is non-conforming; do not rely on what each compiler does.

  • Not every file ends with a newline. Both compilers read a last record with no terminator as a normal record, but check your files and other compilers.

See also Best Practices and Style, Common Gotchas and Pitfalls, Error Handling and Program Termination for the program-level view of iostat=, stop and error stop, and Testing, Debugging and Profiling for running a build with the runtime checks that catch an out-of-range internal write.