Exception handling when the file does not exist - fortran

I have a Fortran program that starts with opening and reading data from a .txt file.
At the end of the program a new file is written, which replaces the old file (that was originally imported).
However it can occur that the file that needs to be opened does not exists, for that case the variables that should be imported from the .txt file should be 0.
I thought by doing this with the code below, however this does not work and the script is aborted when the file history.txt does not exists.
How can I let the script set default values to my variables when the history.txt file does not exists?
OPEN(UNIT=in_his,FILE="C:\temp\history.txt",ACTION="read")
if (stat .ne. 0) then !In case history.txt cannot be opened (iteration 1)
write(*,*) "history.txt cannot be opened"
KAPPAI=0
KAPPASH=0
go to 99
end if
read (in_his, *) a, b
KAPPAI=a
KAPPASH=b
write (*, *) "KAPPAI=", a, "KAPPASH=", b
99 close(in_his)
The file that is imported is pretty simple and looks like:
9.900000000000006E-003 3.960000000000003E-003

I would use IOSTAT as stated by #Fortranner. I would also set defaults before trying to open the file and I tend not to use goto's. As in:
program test
implicit none
integer :: in_his, stat
real :: KAPPAI, KAPPASH
in_his = 7
KAPPAI = 0
KAPPASH = 0
OPEN(UNIT=in_his, FILE="history.txt",ACTION='read',IOSTAT=stat,STATUS='OLD')
if (stat .ne. 0) then
write(*,*) "history.txt cannot be opened"
stop 1
end if
read (in_his, *) KAPPAI, KAPPASH
close(in_his)
write (*, *) "KAPPAI=", KAPPAI, "KAPPASH=", KAPPASH
end program test

Another way is to use an inquire statement and check for the existence of the file before you try to open it. This would set a logical variable that could be used in an IF statement to handle the two cases: 1) open file and read values, or 2) set default values w/o opening the file. Or set the default values first, then have the IF statement only handle the case of opening the file and reading the values.

Set iostat in the open statement and handle the case where it is nonzero.

There are two ways to do this. One is using IOSTAT specifier in the OPEN statement like Fortranner and Timothy Brown suggested. The other is to use the ERR specifier in the OPEN statement which lets you specify a label to which the program will transfer control in the even of an error:
OPEN(UNIT=in_his,FILE="C:\temp\history.txt",ACTION="read",STATUS='OLD',ERR=101)
...
101 CONTINUE
The label must be in the same scoping unit as the OPEN statement.

Related

Fortran execute_command_line does not return result

I need to create a file in fortran and then read the data in the file.
call execute_command_line('pwd > workdir.dat')
open(unit=10, file='workdir.dat', status='replace', IOSTAT=open_stat)
if (open_stat /= 0) stop "Reading workdir.dat file Error"
read(10,"(A)") workdir
close(10)
However, workdir.dat is empty when I try to open it, giving me serious open error. It seems that the system only flush the content of workdir.dat to the file at the end of the program. How do I make sure workdir.dat is ready to use before open ?
The open statement includes the clause status=replace which, in effect, tells the run time system that to discard the file's contents and write them anew. To be precise, the language standard states wrt to the status specifier on an open statement:
If REPLACE is specified and the file does exist, the file is deleted,
a new file is created with the same name, and the status is changed to
OLD.
Change the clause to status=old which is the correct specification for this case.

GFortran I/O error 5002 while reading a direct access file

I have a little Problem reading a file in Fortran. As you can see I am lopping over a file reading certain records with a specific length.
What happens is, when it comes to a certain record I'm getting an IOSTAT Error 5002. Now my question is what does this error mean: is it end of file or there is no record left or something else? Can I ignore it?
I am using MinGW GFortran 4.8.0.
Here's the code:
PROGRAM test_read
INTEGER*4 HCM_error
DOUBLE PRECISION N_Record(22)
CHARACTER*8 C_Record(22)
EQUIVALENCE (N_Record,C_Record)
OPEN (UNIT=11, FILE='C:/BORDER/D__HOL.000',STATUS='OLD', ACCESS='DIRECT',RECL=176, ACTION='READ', IOSTAT=IOS)
HCM_error=0
DO N_rec = 1, 2000
READ (11, REC=N_rec, IOSTAT=IOS) C_Record
WRITE(*,*) "|",IOS,' ',N_rec,' ',N_record(21),' ',N_record(22),"|"
!End of file reached (or non existing record) ?
IF ((IOS .LT. 0) .OR. (IOS .EQ. 36)) EXIT
IF (IOS .NE. 0) THEN
!Error in (border-) line data
HCM_Error = 1049
EXIT
END IF
END DO
CLOSE(UNIT=11)
WRITE (*,*) HCM_error
END PROGRAM
The non-zero values returned by an iostat= specifier are not portable across compilers. If you wish to determine what a particular code means then you have two options:
read the compiler's documentation (if it exists)
use the iomsg= specifier with a character variable
In this case, when you tried iomsg= you got the message "Non-existing record number". So, problem solved.
Well, almost. There's more to say.
You may be surprised that you are going through records in turn in direct access, but are reaching a "no record" state without first reaching an "end of file" state. You are testing (IOS .LT. 0) with a comment "!End of file reached".
When reading a file connected for direct access, the end of file condition doesn't arise.
What can you do to detect that the record isn't a valid number, beyond the end of the file? Not much, portably, but any positive number from iostat= indicates an error condition. You know now, though, what this particular 5002 means.
I should probably also add that the character variable for iomsg= is defined by the transfer statement only if there isn't success. Consider it only if you know the transfer failed.

WRITE statement

My program works with a set of files (several millions). All the files were created earlier with some other code. Some of the files are empty, some have values; all of them have 'OLD' status. My program has to
open one of the files;
add some value to the END of THE FILE if the file contains numbers already or just put a first value if the file is empty;
close the file and go to another file processing.
Right now, if the file is non-empty, the program erase the file's previous content and just write a new value. I think, in order TO ADD a value to the end of existing non-empty file I need to use some clause in OPEN or WRITE statement in addition to the 'OLD' status. Which ones? Thank you.
It would be easier with a MWE, but nonetheless, what you could do is something like that, using the append keyword
open(unit=file_unit, file=filename, status='old', access='append')
You could try it on this simple example adapted from the Fortran Wikibook to see how it works
program write
implicit none
integer :: i, j
integer, parameter :: out_unit=10
print*,"Enter two integers:"
read (*,*) i, j
open (unit=out_unit, file="results.txt", action="write", status="old", access="append")
write (out_unit,*) "The product of",i," and",j
write (out_unit,*) "is",i*j
close (out_unit)
end program write

Single command to open a file or create it and the append data

I would like to know if in Fortran it is possible to use just a single command (with options/specifiers) to do the following:
open a file if it exists and append some data
(this can be done with: open(unit=40,file='data.data',Access = 'append',Status='old') but if the file does not exist a runtime error is issued)
create the file if it does not exist and write some data.
I am currently using inquire to check whether the file exist or not but then I still have to use the open statement to append or write data.
As far as I am aware of, the only safe solution is to do the way you're already doing it, using different open statements for the different cases:
program proba
implicit none
logical :: exist
inquire(file="test.txt", exist=exist)
if (exist) then
open(12, file="test.txt", status="old", position="append", action="write")
else
open(12, file="test.txt", status="new", action="write")
end if
write(12, *) "SOME TEXT"
close(12)
end program proba
You may be interested in my Fortran interface library to libc file system calls (modFileSys), which could at least spare you the logical variable and the inquire statement by querying the file status directly:
if (file_exists("test.txt")) then
...
else
...
end if
but of course you can program a similar function easily yourself, and especially it won't save you from the two open statements...
open(61,file='data.txt',action='write',position='append')
write(61,*) 'hey'
close(61)
This will append to an existing file, otherwise create and write. Adding status='unknown' would be equivalent.
if you replace the status from 'old' to 'unknown' then you will not get the run time error if the file exists or now.
Thanks
In open statement add the attribute access as follows;
Open(unit=031,file='filename.dat',form='formatted',status='unknown',access='append')
The above statement will open the file without destroying old data and write command will append the new lines in the file.
The simplest solution for fortran 90.

Write and read access=stream files in fortran

I have a shell script from which I pass a binary file to a fortran program such that
Mth=$1
loop=1
it=1
while test $it -le 12
do
Mth=`expr $Mth + $loop`
file="DataFile"$Mth".bin"
./fort_exe ${Yr} ${nt} ${it}
# Increment loop
it=`expr $it + 1`
done
This script is used to pass 12 files within a do loop to the fortran program. In the fortran program, I read the binary file passed from the shell script and I am trying to write a 2nd file which would compile in a single file all the data that was read from the consecutive files e.g.
!Open binary file passed from shell script
open(1,file='Datafile'//TRIM{Mth)//.bin',action='read',form='unformatted',access='direct', &
recl=4*x*y, status='old')
! Open write file for t 1. The status is different in t 1 and t > 1 so I open it twice: I guess there is a more elegant way to do this...
open(2,file='Newfile.bin',action='write',form='unformatted', &
access='stream', position='append', status='replace')
irec = 0
do t = 1, nt
! Read input file
irec = irec + 1
read(1,rec=irec) val(:,:)
! write output file
irecW= irec + (imonth-1)*nt
if ( t .eq. 1) write(2,pos=irecW) val(:,:)
! Close file after t = 1, update the status to old and reopen.
if ( t .eq. 2) then
close (2)
open(2,file='Newfile.bin',action='write',form='unformatted', &
access='stream', position='append',status='old')
endif
if ( t .ge. 2) write(2,pos=irecW) val(:,:)
enddo
I can read the binary data from the first file no problem but when I try and read from another program the binary data from the file that I wrote in the first program such that
open(1,file='Newfile.bin',action='read',form='unformatted', &
access='stream', status='old')
irec=0
do t = 1, nt
! Read input file
irec = irec + 1
read(1,pos=irec) val(:,:)
write(*,*) val(:,:)
enddo
val(:,:) is nothing but a list of zeros. This is the first time I use access=stream which I believe is the only way I can use position='append'. I have tried compiling with gfortran and ifort but I do not get any error messages.
Does anyone have any idea why this is happening?
Firstly, I do not think you need to close and reopen your output file as you are doing. The status specifier is only relevant to the open statement in which it appears: replace will delete Newfile.bin if it exists at that time, before opening a new file with the same name. The status is implicitly changed to old, but this does not affect any operations done to the file.
However, since your Fortran code does not know you run it 12 times, you should have a way of making sure the file is only replaced the first time and opened as old afterwards; otherwise, Newfile.bin will only contain the information from the last file processed.
As for reading in the wrong values, this most likely occurs because of the difference between direct access (where you can choose a record length) and stream access (where you cannot). With stream access, data is stored as a sequence of "file storage units". Their size is in general compiler-dependent, but is available through the module iso_fortran_env as file_storage_size; it is usually 8 bits. This means that each entry will usually occupy multiple storage units, so you have to take care that a read or write with the pos = specifier does not access the wrong storage units.
Edit:
Some example code writing and reading with stream access:
program stream
use, intrinsic :: iso_fortran_env
implicit none
integer :: i, offset
real(real32), dimension(4,6) :: val, nval
open(unit=2, file='Newfile.bin', action='readwrite', form='unformatted', &
access='stream', status='replace')
do i = 1,2
call random_number(val)
write(2) val
enddo
! The file now contains two sequences of 24 reals, each element of which
! occupies the following number of storage units:
offset = storage_size(val) / file_storage_size
! Retrieve the second sequence and compare:
read(2, pos = 1 + offset*size(val)) nval
print*, all(nval == val)
close(2)
end program
The value true should be printed to the screen.
Note also that it's not strictly necessary to specify a pos while writing your data to the file, because the file will automatically be positioned beyond the last record read or written.
That said, direct or stream access is most beneficial if you need to access the data in a non-sequential manner. If you only need to combine input files into one, it could be easier to write the output file with sequential access, for which you can also specify recl and position = 'append'.
You can check for the existence of a file in standard Fortran, by using the inquire statement:
logical :: exist
inquire(file="test.dat", exist=exist)
if (exist) then
print *, "File test.dat exists"
else
print *, "File test.dat does not exist"
end if
Alternatively you can have a look at the modFileSys library which provides libc like file manipulation routines.
As for appending and streams: Appending files is also possible when you use "classical" record based fortran files, you do not have to use streams for that.