More on input/output
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
- Explain why the above code uses
"(a)"
instead of*
as the format descriptor. - 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.
- File format
- Text: human-readable but slow
- Binary: machine-readable and fast
- Contents
- Matrix elements only = the minimal file
- Matrix elements with some information (size etc.) = useful when reading
- Index
- With index (triplet: \(i\), \(j\), \(A_{ij}\))
- No index (only \(A_{i,j}\))
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
- Modify the above program to read a matrix from a text file.
- Modify the above program to save a binary file.
- Save a matrix with indices.
Back to index.html.