ZetCode

Fortran files

last modified November 14, 2026

A variable disappears when the program ends; a file does not. A file is a named collection of data stored outside the program, and in Fortran it is seen as a sequence of records, where a text file has one record per line. Before a program can use a file it must open it, which connects the file to a small integer called a unit; from then on every read and write mentions the unit instead of the name. This article shows how to create, read, append and copy text files, how to choose the right status and action, how to notice a failed open with iostat and iomsg, and how to make a temporary file that disappears when it is closed.

Writing a text file

The open statement below uses three specifiers. file= gives the name, status='replace' says that the file should be created, or emptied if it already exists, and newunit=u asks the runtime to pick a free unit and store its number in u. The returned number is negative, which is the convention for units reserved this way; it cannot clash with the small positive numbers that older programs use by hand. The two write statements then place two integers in each record, and close writes the remaining data out and releases the unit.

f1.f90
program f1
    implicit none
    integer :: u, i

    open (newunit=u, file='squares.txt', status='replace')
    do i = 1, 5
        write (u, '(i3, i6)') i, i * i
    end do
    close (u)
    print *, 'wrote 5 records'
end program f1

The format (i3, i6) gives the first column three characters and the second six, so the numbers line up on their last digit. The transcript runs the program and then shows the file with cat, a standard Unix command that copies a file to the screen. Nothing about the output depends on the compiler: the format fixes every character, including the two blanks before each single-digit square.

$ gfortran f1.f90 -o f1
$ ./f1; cat squares.txt
 wrote 5 records
  1     1
  2     4
  3     9
  4    16
  5    25

Reading a file line by line

Reading is the mirror image: open with status='old' insists that the file already exists, and each read takes one record. The format '(a)' copies the whole record into a character variable without trying to interpret it, which is the text equivalent of reading a line in another language. The loop has no counter because the number of records is not known in advance; it stops when iostat reports that the last record has been passed.

f2.f90
program f2
    implicit none
    character(len=40) :: line
    integer :: u, ios, n

    open (newunit=u, file='squares.txt', status='replace')
    write (u, '(a)') 'one   1'
    write (u, '(a)') 'two   4'
    write (u, '(a)') 'three 9'
    close (u)

    n = 0
    open (newunit=u, file='squares.txt', status='old')
    do
        read (u, '(a)', iostat=ios) line
        if (ios /= 0) exit
        n = n + 1
        print *, n, '[', trim(line), ']'
    end do
    close (u)
end program f2

The first half of the program writes the file that the second half reads, so the example can run in an empty directory. The status is zero after each successful read and negative at the end of the file, which is why the test is ios /= 0 rather than ios < 0: a positive status would mean a damaged record, and that should also stop the loop. Because the records are read as text, their content does not matter to the program; the brackets show that the trailing blanks of line were trimmed away before printing.

$ gfortran f2.f90 -o f2
$ ./f2
           1 [one   1]
           2 [two   4]
           3 [three 9]

Existing files and open errors

A real program cannot assume that the file it wants is there, or that the directory is writable. Two tools answer such questions before the program commits to a course of action: inquire reports properties of a file without opening it, and the iostat= and iomsg= specifiers of open turn a failure into a status code and a message instead of an unconditional stop. iostat is zero on success and positive or negative on failure, and the character variable given to iomsg receives a description written by the runtime. The related specifier action= states what the program intends to do with the file: 'read', 'write' or 'readwrite'. It is a promise that the runtime can check, and an open that asks for 'read' on a file that does not exist, or for 'write' on a read-only file, fails at once instead of at the first transfer.

f3.f90
program f3
    implicit none
    integer :: u, ios
    logical :: exists
    character(len=100) :: msg

    open (newunit=u, file='report.txt', status='replace')
    write (u, '(a)') 'Fortran file test'
    close (u)

    inquire (file='report.txt', exist=exists)
    print *, 'exists: ', exists
    inquire (file='missing.txt', exist=exists)
    print *, 'missing exists: ', exists

    open (newunit=u, file='missing.txt', status='old', iostat=ios, iomsg=msg)
    print *, 'open status:', ios
    if (ios /= 0) print *, trim(msg)
end program f3

The first file exists because the program has just written it, while the second one does not, and inquire reports the difference without any error. The open of the missing file then fails with status 2, and the message says what went wrong in words. The number itself is processor dependent, so a portable program tests it against zero and prints the message rather than relying on the value; the text of the message is also specific to the runtime, which is why the article quotes it only as an example.

$ gfortran f3.f90 -o f3
$ ./f3
 exists:  T
 missing exists:  F
 open status:           2
 Cannot open file 'missing.txt': No such file or directory

Appending to a file

Opening a file for writing normally starts at its beginning and overwrites whatever was there. The specifier position='append' changes that: the file is opened with the position after the last record, so every write adds to the end and the earlier contents survive. The program below builds a small log in three separate opens, which is how a program that runs many times can keep adding lines to the same file.

f4.f90
program f4
    implicit none
    integer :: u
    character(len=80) :: line
    integer :: ios, lines

    open (newunit=u, file='log.txt', status='replace')
    write (u, '(a)') 'start'
    close (u)

    open (newunit=u, file='log.txt', position='append')
    write (u, '(a, i0)') 'step ', 1
    write (u, '(a, i0)') 'step ', 2
    close (u)

    open (newunit=u, file='log.txt', position='append')
    write (u, '(a)') 'end'
    close (u)

    lines = 0
    open (newunit=u, file='log.txt', status='old')
    do
        read (u, '(a)', iostat=ios) line
        if (ios /= 0) exit
        lines = lines + 1
        print *, lines, trim(line)
    end do
    close (u)
end program f4

The first open creates the file with one record, the second adds two, and the third adds one more, so the final file has four records. Without position='append' the second open would place the position at the start and start would be overwritten by step 1. Note that status='replace' appears only in the first open; using it again later would delete the records written before.

$ gfortran f4.f90 -o f4
$ ./f4
           1 start
           2 step 1
           3 step 2
           4 end

Reading numbers from a file

A list-directed read from a file works like a list-directed read from the keyboard, and it consumes one whole record per statement. That is the point that surprises newcomers: if a record holds two numbers and the statement asks for one variable, the second number is lost with the rest of the record. The program below therefore writes one value per line and reads them in a loop, accumulating a sum, a maximum and a count until the file ends.

f5.f90
program f5
    implicit none
    real :: x, total, vmax
    integer :: u, ios, n

    open (newunit=u, file='values.txt', status='replace')
    write (u, '(a)') '1.5'
    write (u, '(a)') '2.5'
    write (u, '(a)') '3.0'
    write (u, '(a)') '-1.0'
    close (u)

    total = 0.0
    vmax = -huge(1.0)
    n = 0
    open (newunit=u, file='values.txt', status='old')
    do
        read (u, *, iostat=ios) x
        if (ios /= 0) exit
        n = n + 1
        total = total + x
        vmax = max(vmax, x)
    end do
    close (u)
    print *, 'values:', n
    print *, 'total: ', total
    print *, 'mean:  ', total / n
    print *, 'max:   ', vmax
end program f5

Four values produce a total of 6, a mean of 1.5 and a maximum of 3. The starting value of vmax is -huge(1.0), the most negative real of the default kind, so the first value always replaces it. If a record cannot be converted, the status becomes positive and the loop stops as if the file had ended; a program that must survive damaged data reads whole lines and parses them itself, as the CSV article shows.

$ gfortran f5.f90 -o f5
$ ./f5
 values:           4
 total:    6.00000000    
 mean:     1.50000000    
 max:      3.00000000    

Copying a file

Copying a text file means reading one record and writing it to another file until the input ends. Two units are open at the same time, each with its own position, and the loop transfers the records one by one. The blank padding of the fixed-length line is removed with trim before the write, so the copy does not accumulate trailing spaces that the original did not have.

f6.f90
program f6
    implicit none
    character(len=80) :: line
    integer :: src, dst, ios, n

    open (newunit=src, file='source.txt', status='replace')
    write (src, '(a)') 'alpha'
    write (src, '(a)') 'beta'
    write (src, '(a)') 'gamma'
    close (src)

    open (newunit=src, file='source.txt', status='old')
    open (newunit=dst, file='copy.txt', status='replace')
    n = 0
    do
        read (src, '(a)', iostat=ios) line
        if (ios /= 0) exit
        write (dst, '(a)') trim(line)
        n = n + 1
    end do
    close (src)
    close (dst)
    print *, 'copied', n, 'records'
end program f6

The input unit is opened with status='old' and the output unit with status='replace', so the source is protected from being emptied by mistake. Three records are transferred and the program reports the count. Closing both units matters for a program that copies many files, because the number of units a process may have open at once is limited; closing also guarantees that the last record actually reached the disk before the program moves on.

$ gfortran f6.f90 -o f6
$ ./f6
 copied           3 records

A formatted data file

Files that other programs read are usually written with a fixed layout rather than with list-directed output, because the layout must stay the same from run to run. The program below writes a header and three rows with (a8, f8.2), so the names occupy eight characters and the scores eight more with two decimals. The same widths are used for the header, which is why the word score sits above the numbers.

f7.f90
program f7
    implicit none
    character(len=10) :: name(3)
    real :: score(3)
    integer :: u, i

    name = ['alice', 'bob  ', 'carol']
    score = [91.5, 78.25, 84.0]
    open (newunit=u, file='scores.txt', status='replace')
    write (u, '(a8, a8)') 'name', 'score'
    do i = 1, 3
        write (u, '(a8, f8.2)') trim(name(i)), score(i)
    end do
    close (u)
    print *, 'wrote scores.txt'
end program f7

The header uses a8 twice because both items are character strings; the data rows use a8 and f8.2 because the second item is a real number. Mixing the two up is a common mistake: the compiler cannot check the format against the values at compile time, and a wrong descriptor stops the program at run time. The file on disk contains exactly the columns shown by cat, with four blanks in front of name because the word has four letters in a field of eight.

$ gfortran f7.f90 -o f7
$ ./f7; cat scores.txt
 wrote scores.txt
    name   score
   alice   91.50
     bob   78.25
   carol   84.00

Temporary and scratch files

Some files are working space rather than results, and they should not be left behind. Two mechanisms cover that. A file opened normally can be removed when it is closed, with close(unit, status='delete'), and a file opened with status='scratch' has no name at all: the runtime creates it, lets the program use it, and deletes it when the program closes it or exits. Both are useful for intermediate results that are too large to keep in memory.

f8.f90
program f8
    implicit none
    integer :: u
    logical :: exists

    open (newunit=u, file='temporary.txt', status='replace')
    write (u, '(a)') 'scratch data'
    close (u, status='delete')
    inquire (file='temporary.txt', exist=exists)
    print *, 'file still there:', exists

    open (newunit=u, status='scratch')
    write (u, '(a)') 'invisible'
    rewind (u)
    print *, 'scratch works'
    close (u)
end program f8

After the first close the file is gone, so inquire reports F. The scratch file is invisible in the directory from the start, and rewind moves its position back to the first record so that it could be read again. A scratch file cannot be reopened later, since it has no name; if the data must survive the close, a named file is the only option.

$ gfortran f8.f90 -o f8
$ ./f8
 file still there: F
 scratch works

Next steps

Text files store characters, but numbers can also be written in their internal binary form, which is smaller, faster and exact. The stream and binary files article covers access='stream', unformatted records and direct access. The namelists article shows a compact format for configuration files, and the CSV and text data article reads a whole table into arrays. The formats used here are explained in the formatted output article, and the reading input article applies the same iostat loop to the keyboard.

Source

Fortran 2018 standard (J3/18-007r1)

GNU Fortran manual

Fortran-lang: learning resources

In this article we have opened files with newunit, chosen between status='replace', 'old' and 'scratch', written and read text records, appended to a log, copied a file, produced a fixed-layout data file, and handled a failed open with iostat and iomsg. The previous article, Reading input in Fortran, shows the same statements reading from the standard input.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Fortran tutorials.