Fortran, like most programming languages, uses the refrigerator model for file access. Like a refrigerator, a file must be opened before we can put anything into it (write) or take anything out (read). After we're done reading or writing, it must be closed.
A file is essentially a sequence of bytes stored on a disk or other non-volatile storage device.
Access to files is, for the most part, sequential, meaning we start reading a file at the first byte, and go forward from there. After we read the first byte, the next read operation automatically gets the second, and so on. It is possible to control the order in which we read or write data to a file, but doing non-sequential access is a bit more cumbersome than it is with an array. Unlike array access, it takes separate statements to go to a different location within the file and then read or write data.
Before we can access a file from Fortran, we must open it. The open statement creates a new unit, which can then be used in read and write statements to input from or output to the file.
As you saw in the section called “Fortran Terminal Input/Output (I/O)”, all input and output in Fortran uses unit numbers.
The unit number is Fortran's way of representing the more general concept of a file handle. The term "file handle" is a metaphor for a mechanism used to grasp or control something. Just like a suitcase or door has a handle that allows you to manipulate it, files in Fortran and other languages have conceptual "handles" for manipulating them.
You might be wondering, isn't this what the filename is for? To some extent, yes, but when we're reading or writing a file, we need more than just the name. We need to keep track of where we are in the file, for example.
The open statement creates a structure containing information about the file, such as its name, whether we're reading or writing to is, where we are in the file at a given moment, etc. As a Fortran programmer, you do not need to keep track of this information yourself. The compiler and operating system take care of all of this for you. All you need in order to work with an open file is the unit number. This unit number is your handle to the file.
It is your job as a Fortran programmer to choose a unique unit number for each file you open. Low-numbered unit numbers such as 0, 1, and 2 are reserved for special units like INPUT_UNIT, OUTPUT_UNIT, and ERROR_UNIT. Most Fortran programmers use 10 as the lowest unit number, and go up from there if they need more than one file open at a time.
module constants integer, parameter :: & MAX_PATH_LEN = 1024, & CHROMOSOME_UNIT = 10 end module program files implicit none integer open_status, close status character(MAX_PATH_LEN) :: filename filename = 'input.txt' open (unit=CHROMOSOME_UNIT, file=filename, status='old', & iostat=open_status, action='read', position='rewind') if ( open_status /= 0 ) then print *, 'Could not open ',filename,' for reading.', & 'unit = ', unit stop endif ... close(CHROMOSOME_UNIT, iostat=close_status) if ( close_status /= 0 ) then print *, 'Error: Attempt to close a file that is not open.', & 'unit = ', CHROMOSOME_UNIT stop endif end program
The open statement uses a number of tags to specify which file to open and how it will be used.
Required tags:
Optional tags:
The close statement makes sure any writes to a file opened for writing are complete, and then disables the unit. About the only way a close can fail is if the unit does not represent an open file. This generally means there's a bug in the program, since a close statement should only be attempted if the open succeeded.
The read statement works for files exactly as it does for the standard input. Recall that you can actually make the standard input refer to a file instead of the keyboard by using redirection in the Unix shell:
shell> asg02 < input.txt
The above example reads from standard input using a statement
such as read (*,*) variable
.
Recall that the first '*' represents the default unit (standard
input) and the second represents the default format.
When reading from a file that was opened by the program, we simply replace the first '*' with an explicit unit number.
We also introduce here the use of the iostat tag. The iostat variable will receive 0 is the read is successful, and a non-zero error code if it failed (at end of file, file is not open, etc.) Technically, the iostat tag could and should be used when reading from the standard input as well, but it was omitted in Chapter 18, Basic Terminal Input/Output (I/O) for simplicity.
integer :: read_status character(MAX_CHROMOSOME_LEN) :: chromosome1 read (CHROMOSOME_UNIT, *, iostat=read_status) chromosome1 if ( read_status /= 0 ) then print *, 'Error reading file, unit = ', CHROMOSOME_UNIT stop endif