Advanced File IO

The basic file input/output commands previously covered are sufficient for many programs, but far more control is possible through the use of other commands and options. We will describe on the most common here.
A detailed overview of file IO can be found in Intel’s documentation.

OPEN Options

Several options to OPEN are related to error checking and are common among several file IO commands:

IOSTAT=ios !Returns status into integer variable ios.  Nonzero value for failure.
IOMSG=iomesg !Returns into a character variable msg an informative message on error
ERR=label  !Jumps to the statement labeled `label` if an error occurs.
END=label  !Jumps to the statement labeled `label` on end of file.

The following are specific to OPEN and describe the file type:

STATUS=stat

The value of stat can be ‘OLD’, ‘NEW’ , ‘REPLACE’, ‘SCRATCH’, or ‘UNKNOWN’. (As usual, the strings are not case-sensitive.) The default is ‘UNKNOWN’ (read/write permission). If ‘OLD’ it must exist, and if ‘NEW’ it must not exist. A ‘SCRATCH’ file is automatically deleted after being closed.

POSITION=pos

Position pos is ‘ASIS’ (the default), ‘REWIND’, or ‘APPEND’. REWIND returns the file pointer to the top, which will cause the file to be overwritten by new data. APPEND leaves it at the end of the file so new data can be added.

FORM=fmt

The permitted values for fmt are ‘FORMATTED’ (the default, for a text file) or ‘UNFORMATTED’ (a system-dependent binary format). This term is not related to whether the text is “formatted” for printing or not.

ACCESS=acc

The access acc can be ‘SEQUENTIAL’ (the default), ‘DIRECT’, or ‘STREAM’. Direct files must be unformatted. Unless access is ‘STREAM’, an unformatted file will have a header and footer that is specific to a compiler and platform and may not be portable. This is a relic of magnetic tape drives and allowed them to backspace and skip forward in the tape. For a binary-format file similar to that produced by C/C++ programs and interchangeable with them, use

OPEN(UNIT=iunit,FILE=fname,ACCESS=stream,FORM=unformatted)

Inquire

The INQUIRE statement tests the status of a file. Most usually we wish to check whether the file exists, or is already open, before we attempt to open it.

INQUIRE(UNIT=iunit,options)

or

INQUIRE(FILE=fname,options)

So we can inquire by unit or name but not both.

Common Options to Inquire

Several of the options to INQUIRE are similar to those of OPEN. Others are more specific.

IOSTAT=ios     ! Like open, ios must be integer
ERR=label      ! Like open
EXIST=exists   ! Returns .true. or .false. into logical variable exists
OPENED=is_open ! Returns .true. or .false. into logical variable is_open

READ Options for Files

The options to READ and WRITE include those already described for console IO, as well as some that are only relevant to files. As an example, for ACCESS=STREAM files only, the position specifier can be used to start reading from a particular location on the file.

POS=p

See documentation for more options.

CLOSE

The complete form of the CLOSE command is

CLOSE(UNIT=iunit,IOSTAT=ios,ERR=err,STATUS=stat,IOMSG=iomesg)

STATUS can be ‘KEEP’ (default) or ‘DELETE’. UNIT, IOSTAT, IOMSG, and ERR are like the corresponding options to OPEN.

REWIND

An open unit can be rewound. This places the file pointer back to the beginning of the file. The default is to rewind a file automatically when it is closed. If you want to rewind the file to reread it, use

REWIND(iunit)

REWIND is convenient if the program must handle files whose lengths may vary. Read through the file without storing any variables, count the number of lines, rewind, then allocate any arrays needed.

If your input files will always be of known length this isn’t necessary (or efficient), but often file length could vary with different data.

Example

program file_io_demo
implicit none
integer :: iunit, ios, nlines, sz
integer :: n
logical :: file_exists
character(len=128) :: fname, iomerr
real, allocatable, dimension(:) :: time, obs

   fname="cpi.csv"
   iunit=10

   inquire(file=fname,exist=file_exists,size=sz)
   if (sz==0) then
      stop "Empty file"
   else if ( .not. file_exists ) then
      stop "File "//trim(fname)//" not found."
   endif

   open(iunit,file=fname,iostat=ios,status='UNKNOWN')
      if (ios /= 0) then
         stop "Can't open file "//trim(fname)
      endif
   nlines=0
   do
   read(iunit,*,iostat=ios,iomsg=iomerr,end=1)
      if (ios /= 0) then
         stop "Error reading file:"//trim(iomerr)
      else
          nlines=nlines+1
      endif
   end do
1  continue

   rewind(iunit)

   allocate(time(nlines),obs(nlines))

   do n=1,nlines
      read(iunit,*) time(n),obs(n)
   enddo

end program

QUIZ

Why do I increment nlines after the read? What would I do if I had one or more header lines?

Answer

I need to wait until the line has been successfully read before I count it. Consider an empty file (zero lines). I will immediately encounter end of file, so I want to exit then. For a one-line file, it reads the first line and I count that, then next time around it hits end of file and exits. Nlines=1 then, which is correct. The rest follows by induction. For the second question, if I have header lines then I must use one READ per line to move through them before entering the loop to read data. I can ignore the contents of the line if they are unneeded by providing no variable to store the data.

Exercise

Read the mydata.txt file but don’t assume you know how long it is. Inquire whether it is present, then read it through just to count the number of lines. Rewind the file to read the data.

Example Solution

program file_io_demo
implicit none
integer :: iunit, ios, nlines, sz
integer :: n
logical :: file_exists
character(len=128) :: fname, iomerr
integer :: item1, item2, item3

   fname="mydata.txt"
   iunit=10

   inquire(file=fname,exist=file_exists,size=sz)
   if (sz==0) then
      stop "Empty file"
   else if ( .not. file_exists ) then
      stop "File "//trim(fname)//" not found."
   endif

   open(iunit,file=fname,iostat=ios,status='UNKNOWN')
      if (ios /= 0) then
         stop "Can't open file "//trim(fname)
      endif
   nlines=0
   do
   read(iunit,*,iostat=ios,iomsg=iomerr,end=1)
      if (ios /= 0) then
         stop "Error reading file:"//trim(iomerr)
      else
          nlines=nlines+1
      endif
   end do
1  continue

   rewind(iunit)

   do n=1,nlines
      read(iunit,*) item1, item2, item3
   enddo

end program

Previous
Next