More on input/output

Yutaka Masuda

February 2020

Back to index.html.

More on input/output

Basic usage

When reading or writing a file, you have to “open” the file. It is an action to link a unit-number (an integer code) to a file name. Each file should have a unique unit-number. After using the file, you should “close” the file so that the link is dissolved. When the program stops, all the opened files are closed.

To open a file, use the open statement. It has given unit-number and a file name. The unit number is pre-defined by you, or you do not care about the unit-number using newunit=.

! user-provided unit number
open(10,file="data.txt")

! can use an integer variable as a unit number
integer :: unit
unit = 10
open(unit,file="data.txt")

! ask the program to assign arbitrary unit number
integer :: unit
open(newunit=unit)

! put the unit number to close the file
close(unit)

To save your data, use the write statement, which has 2 options: a unit-number and a format. The output is the same as the print statement except for being saved into the file. The statement has any number of variables as follows.

integer :: i,j
...
write(unit,"(2i5)") i,j

The read statement reads the data from the unit. The usage is similar to write.

integer :: i,j
...
read(unit,*) i,j

If it fail to read the data (wrong format, end of the file, mechanical error in the dard drives etc.), the program stops immediately. Using the iostat= option with an integer variable, the program does not stop, and you get the error code in the variable. When you meet the end of the file, info becomes negative.

integer :: i,j,info
...
read(unit,*,iostat=info) i,j
if(info<0) then
   print *,"end of file"
else
   print *,i,j
end if

File open and close

Open

The open statement accepts many options to handle various files. Here is a table for frequently-used options for open. See textbooks or the standard for the complete list.

Options for open Possible values
unit= unit number (optional)
newunit= integer variable for unit number
file= file name
status= "old" for an existing file; "scratch" for temporary file
position= "append" to add new data to the file
form= "formatted" for text file; "unformatted" for non text file
access= "sequential" for sequential access; "stream" for byte-wise access
iostat= integer variable for error status

Here are typical use-cases of open.

! typical text file for reading or writing
open(newunit=unit, file="data.txt")

! for reading
open(newunit=unit, file="data.txt", status="old")

! append data to the existing file
open(newunit=unit, file="data.txt", position="append")

Using status="scratch", the file is automatically removed when closed. It is useful to create a temporary file that is needed to store big data while the program is running.

By default, the file is treated as a text file that is readable for humans (form="formatted"). With form="unformatted", the data is stored as a machine-readable form, so-called a binary form. In addition to this option, access="stream" treats the file differently. We will see how we use the binary file in this chapter.

Close

There are a few options in the close statement.

Options for open Possible values
unit= unit number (optional)
status= "delete" for deleting the file

When you put status="delete" in close, the program removes the file.

! standard closing
close(unit)

! removing the file
close(unit, statis="delete")

Binary files

Fortran binary file

Writing a file

Fortran treats a file as a text file by default. When opening a file with form="unformatted", the program stores the data in a binary form. See the following program to see the difference between the formats.

program file
   ! text file
   open(10,file="out.txt")
   ! default format
   write(10,*) 1,2,3
   close(10)

   ! binary file
   open(20,file="out.bin",form="unformatted")
   ! no format descriptor
   write(20) 1,2,3
   close(20)
end program file

For a binary file, you can use write, but you do not need a format descriptor. The content of the files is available using the cat command in Linux.

$ cat out.txt
         1         2         3

$ cat out.bin

The cat command may not show the content of a binary file. You can use less to show the content in a cryptic way.

You can save an array to the file in a simple method.

   integer :: unit
   real :: a(10)
   ...
   open(newunit=unit,file="out.bin",form="unformatted")
   write(unit) a
   close(unit)

Reading a file

You can use read without a format descriptor to read the data in a Fortran binary file. Although the way of reading the data is similar to a text file, there is a strict rule to read the binary file. You must read the file in the same way as you have used with write.

For example, you create a binary file with the following statements.

   integer :: i=1, j=2, k=3
   real :: p=3.14, q=6.28

   open(20,file="out.bin",form="unformatted")
   write(20) i,j,k
   write(20) p,q
   close(20)

To read the file, you should use the same list of data types as in the write statement.

   integer :: i,j,k
   real :: p,q

   open(20,file="out.bin",form="unformatted",status="old")
   read(20) i,j,k
   read(20) p,q
   close(20)

If you read the file in a different format, you meet an error.

   ! invalid
   open(20,file="out.bin",form="unformatted",status="old")
   read(20) i,j,k,p,q
   close(20)

   ! also invalid
   open(20,file="out.bin",form="unformatted",status="old")
   read(20) i
   close(20)

When you have saved an array to the binary file, you should read all elements at the same time.

   integer :: i,unit
   real :: a(10)
   ...
   ! making a file
   open(newunit=unit,file="out.bin",form="unformatted")
   write(unit) a
   close(unit)
   ...
   ! valid
   open(newunit=unit,file="out.bin",form="unformatted")
   read(unit) a
   close(unit)

   ! invalid
   open(newunit=unit,file="out.bin",form="unformatted")
   do i=1,10
      read(unit) a(i)
   end do
   close(unit)

You can use iostat= to detect an error while reading the binary file.

   integer :: unit,info
   real :: a(10)
   ...
   read(unit,iostat=info) a
   if(info/=0) stop

Remarks on Fortran binary files

The access to a Fortran binary file is much more efficient than a text file i.e., faster reading and writing. When writing the binary file, the program puts additional code to the data that you write. In this way, the program detects whether read has the same data types as write. It also means that the additional code requires extra storage. You may see that the size of the file is more significant than expected.

The Fortran binary file may not be portable. If you compile the program using a different compiler in a different system, the program may not read the binary file correctly. Also, the Fortran binary file is different from the binary file used in the C language.

Stream binary file

A binary file opened with sccess="stream" is compatible with the C language. In this tutorial, we do not explain this binary file. Please see textbooks for details.

Internal input/output

There is a case where you want to convert a number written in text to an actual number. For example, you have a character literal “123” as text, and you may expect to convert it to integer 123. Conversely, you may want to convert the actual number (123) to characters (“123”). You can use read and write to do such tasks.

The read statement converts a character literal to numerical values as if the characters were from a file. In the following example, the unit number in read is replaced with a character variable, and read converts the characters to a real number.

   character(len=10) :: str="3.14"
   real :: p

   read(str,*) p

When you convert numerical values to characters, you can use write. Instead of the unit number, you use a character variable in write. In this way, the program generates the characters the same as shown on the screen.

   character(len=10) :: str
   real :: p=3.14

   write(str,*) p

Some algorithms on files

Count the number of lines

In many cases, you do not know the number of lines in a text file. To count the lines, you have to read through the file without reading data. The following program is valid to do this job.

program lines
   integer :: unit,n,info

   n = 0
   open(newunit=unit,file="input.txt")
   do
      read(unit,"(a)",iostat=info)
      if(info/=0) exit
      n = n + 1
   end do
   close(unit)

   print *,"the number of lines = ",n
end program lines

Exercises

  1. Explain why the above code uses "(a)" instead of * as the format descriptor.
  2. Make a function to count the number of lines in a given text file.

Write and read a matrix to/from a file

We often need to save a matrix to a file for the later use. We should consider several options for the convenience of data transfer.

With above options, where are 8 possible combinations to save a matrix. We see only a few options.

Text, plan matrix, no index

Here is the simplest code to save a matrix to a file.

program savemat
   implicit none
   character(len=10) :: filename
   integer :: unit,i,m,n
   real,allocatable :: mat(:,:)

   ! make a matrix: m x n
   m = 5
   n = 3
   allocate(mat(m,n))
   mat = reshape([(i,i=1,m*n)],shape(mat))

   ! save the matrix to the file
   filename = "matrix.txt"
   open(newunit=unit,file=filename)
   write(unit,*) mat
   close(unit)
end program savemat

The text file contains a sequence of numbers and spaces like a vector without the newline code. By default for a single-precision real variable, one value needs at least 13 characters. It should result in a huge file if the matrix is large. It is disadvantage in reading and writing the file.

Another issue is that you don’t know the size of original matrix. You may not reproduce the original matrix when you read the file. To solve this, you can save the size of the matrix at the beginning of the file. Here is a piece of code to do it.

   open(newunit=unit,file=filename)
   write(unit,*) m,n
   write(unit,*) mat
   close(unit)

This is a custom format that we are using only in our programs. It is your responsibility how you read the file.

Exercises

  1. Modify the above program to read a matrix from a text file.
  2. Modify the above program to save a binary file.
  3. Save a matrix with indices.

Back to index.html.