Fortran internal files
last modified November 14, 2026
The read and write statements normally talk to
files on disk or to the keyboard and the screen. They can also talk to a
character variable, which is then called an internal file: writing
into it fills it with formatted text, and reading from it interprets that text
as numbers. No file is opened and nothing leaves memory, so internal files are
the standard Fortran way to convert between numbers and strings, to build a
line of output piece by piece, and to split a line of input into fields.
Writing a number into a string
An internal file is used as the unit of the write
statement, in the place where a file unit number usually stands. The second
argument is a format: a recipe that says how the values should be
laid out. In '(i5)' the letters i stand for an
integer, and 5 is the width of the field reserved for it;
i0 is a special width that uses exactly as many characters as
the number needs. The character variable that receives the text is usually
called a buffer, and it must be at least as long as the text: writing
into a shorter variable is an error, which is why the buffer below is larger
than any result.
program n1
implicit none
character(len=20) :: buf
integer :: n
n = 42
write (buf, '(i0)') n
print *, '[', trim(buf), '] len =', len_trim(buf)
write (buf, '(i5)') n
print *, '[', buf, ']'
write (buf, *) n
print *, '[', buf, ']'
n = -7
write (buf, '(i0)') n
print *, '[', trim(buf), '] len =', len_trim(buf)
end program n1
With i0 the buffer holds the two characters 42 and
the remaining eighteen stay blank, so the trimmed length is 2. With
i5 the number is right-aligned in five characters, giving three
blanks before the digits, and the rest of the buffer is still untouched
padding. The third form, write (buf, *), is list-directed
like print *: the compiler picks the layout, and gfortran pads
the integer to a field twelve characters wide here. The last line shows
that a negative number needs one more character for the minus sign.
$ gfortran n1.f90 -o n1 $ ./n1 [42] len = 2 [ 42 ] [ 42 ] [-7] len = 2
Reading a number from a string
Reading works in the opposite direction: the character variable is the unit
and the values on the right receive what was found in the text. Here the
format is *, the list-directed form, which lets the compiler
parse the text much as it would parse input typed at the keyboard: blanks
separate values, and a decimal point or an exponent is understood. The
optional iostat= argument receives a status code instead of
letting a bad conversion stop the program.
program n2
implicit none
character(len=20) :: buf
real :: x
integer :: ios
buf = '3.25'
x = 0.0
read (buf, *, iostat=ios) x
print *, 'x =', x, 'ios =', ios
buf = '2.5e3'
read (buf, *, iostat=ios) x
print *, 'x =', x, 'ios =', ios
buf = 'abc'
read (buf, *, iostat=ios) x
print *, 'x =', x, 'ios =', ios
end program n2
The first two conversions succeed: 3.25 stays as it is and
2.5e3 becomes 2500. The status is 0 in both cases. The third
conversion fails because abc is not a number; the status is
positive, 5010 on this compiler, and x keeps the value it had
before the read. A positive iostat therefore means "the text did
not match", while a negative one would mean "the text ran out"; both are
non-zero, so testing ios /= 0 covers every failure.
$ gfortran n2.f90 -o n2 $ ./n2 x = 3.25000000 ios = 0 x = 2500.00000 ios = 0 x = 2500.00000 ios = 5010
Building a line from pieces
Converting one number per statement becomes tedious when a line contains
several of them. The usual pattern is to write each piece into a small buffer
with i0, trim it, and append it to a growing line. The program
below produces a comma-separated list of the first five squares, which is the
shape of a CSV field or of a query string.
program n3
implicit none
character(len=80) :: line
character(len=20) :: item
integer :: i
line = ''
do i = 1, 5
write (item, '(i0)') i * i
if (i > 1) line = trim(line) // ','
line = trim(line) // trim(item)
end do
print *, '[', trim(line), ']'
print *, 'len:', len_trim(line)
end program n3
The comma is added before every item except the first, and the buffer is
trimmed on both sides of every concatenation so that the twenty-character
item contributes only its digits. The result is eleven
characters: five numbers and four commas. Without the trims the line would
soon be full of blanks and the width of the buffer would limit how many
numbers fit.
$ gfortran n3.f90 -o n3 $ ./n3 [1,4,9,16,25] len: 11
Parsing a comma-separated line
The reverse task is to read a line whose fields are separated by commas. Since
list-directed input treats a comma as a separator, the simplest approach is to
cut the line at the commas with index, copy each piece into a
small string, and read that string as a number. Empty or malformed fields are
skipped when their status is not zero, which makes the parser forgiving
without any extra error handling.
program n4
implicit none
character(len=60) :: line
character(len=20) :: field
real :: total, v
integer :: p, c, ios, count
line = '12.5, 3.25, 7'
total = 0.0
count = 0
p = 1
do
c = index(line(p:), ',')
if (c == 0) then
field = line(p:)
read (field, *, iostat=ios) v
if (ios == 0) then
total = total + v
count = count + 1
end if
exit
end if
field = line(p:p + c - 2)
read (field, *, iostat=ios) v
if (ios == 0) then
total = total + v
count = count + 1
end if
p = p + c
end do
print *, 'values:', count
print *, 'total: ', total
end program n4
The loop handles every field before a comma, and the branch where
index returns zero handles the last field, which has no comma
after it. Three values are found and the total is 22.75. The position
p moves to the character after the comma each time, so the next
search starts at the beginning of the next field. Reading the field with
* ignores the leading space that follows a comma, which is why
3.25 converts without special treatment.
$ gfortran n4.f90 -o n4 $ ./n4 values: 3 total: 22.7500000
What iostat reports
The status from a read is not just "worked" or "failed". Zero means success, a negative value means that the input ended before all values were read, and a positive value means that something in the text could not be converted. With an internal file, "ended" means that the end of the character variable was reached, so reading two numbers from a string that holds one gives a negative status on the second value, which keeps its previous value.
program n5
implicit none
character(len=20) :: buf
integer :: a, b, ios
a = 0
b = 0
buf = '10 20'
read (buf, *, iostat=ios) a, b
print *, a, b, ios
a = 0
b = 0
buf = '10'
read (buf, *, iostat=ios) a, b
print *, a, b, ios
a = 0
b = 0
buf = '10 x'
read (buf, *, iostat=ios) a, b
print *, a, b, ios
end program n5
The first read fills both variables and reports 0. The second reads
10 into a and then runs out of text for
b, so it reports -1 and leaves b at zero. The third
reads 10 and then meets the letter x, so it reports
5010 and again leaves b alone. The distinction matters when a
loop reads values until the end of a file: -1 is the normal way to stop,
while a positive status deserves a message.
$ gfortran n5.f90 -o n5
$ ./n5
10 20 0
10 0 -1
10 0 5010
A conversion helper
Once the conversion is wrapped in a function, the rest of the program does not have to think about buffers. The function below returns a deferred-length string, so the caller receives exactly the digits and can concatenate them into a message. This is the Fortran equivalent of a "number to string" function in other languages, and it is used later in the article on formatted output.
module convmod
implicit none
contains
pure function int_to_str(n) result(r)
integer, intent(in) :: n
character(len=:), allocatable :: r
character(len=20) :: buf
write (buf, '(i0)') n
r = trim(buf)
end function int_to_str
end module convmod
program n6
use convmod
implicit none
integer :: i
do i = 1, 3
print *, 'row ' // int_to_str(i) // ': ' // int_to_str(i * i)
end do
end program n6
The helper's result is allocated to the trimmed length, so
int_to_str(2) is a one-character string and the concatenation
adds no padding. The loop prints the row number and its square, and the
pattern works for any integer without the caller reserving a buffer. Internal
write is allowed inside a pure function because the file is a
local variable and nothing outside the function is touched.
$ gfortran n6.f90 -o n6 $ ./n6 row 1: 1 row 2: 4 row 3: 9
Next steps
The formatted output article explains
the edit descriptors used in these formats, such as i0,
f8.2 and a, in full. The
strings article covers the declarations and
the trimming functions, and the
reading input article applies the same
iostat technique to data typed at the keyboard, and the
files article shows how the same statements work
on a real file instead of a variable.
Source
Fortran 2018 standard (J3/18-007r1)
Fortran-lang: learning resources
In this article we have used character variables as files: writing numbers into them, reading numbers back, building a line from formatted pieces, splitting a comma-separated record, and reading the status that each transfer returns. The previous article, Fortran string processing, shows how to clean and cut the text before and after the conversion.
Author
List all Fortran tutorials.