WRITE statement - fortran

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

Related

READ from array instead of file

I have an existing Fortran 77 program where the input values are read from an input file.
read (unit=*, fmt=*) value
The read statement automatically jumps to the next line in the file every time it is called.
Is it possible to replace the "reference" index with another data container, like an array?
For example:
read (myarray, fmt=*) value
I tried it but it always reads the first array-element and does not jump automatically to the next element.
I would have to change every read(unit=*, ...) to read(array(i), ...) and increase the i separately to get to the next element.
Since the program is huge, I am looking for a way to keep the existing read statements and just change the source of the data.
So the unit wouldn't be a integer value but a array where every element is a line from my input file.
Does anybody have an idea?
I tried to discribe the problem in code:
(the input_file.input ist just 15 lines with the numbers 1 to 15)
program FortranInput
implicit none
! Variables
integer :: i, inpid
character*130, dimension(100) :: inp_values
character*130 :: value
inpid = 20
! Open File
open(inpid, file='input_file.input')
! Read from file ------------------------------------------------
do i = 1, 15
! read always takes the next line in the file
read(inpid,'(a130)') value
! write each line to new array-element
inp_values(i) = value
! output each line from file to screen
write(*,*) value
end do
close (inpid)
! Read from array -----------------------------------------------
do i = 1, 15
! read always takes the first line in the array
read(inp_values,'(a130)') value
write(*,*) value
end do
end program FortranInput
Yes, in your example you have to always read from the appropriate array element using the (i) syntax. I can't see another way.
However, often you can use a character array as file in multiple records without using the element index. Consider this:
integer :: i, n=15
character*130, dimension(100) :: inp_values
character*130 :: value
integer :: values(100)
do i = 1, n
write(value,*) i
inp_values(i) = value
end do
read(inp_values,'(*(i130,/))') values(1:n)
write(*,*) values(1:n)
or even
read(inp_values,*) values(1:n)
It is important to remember that an internal file does not keep track of the position at which it is opened. The position is only valid within each write or read statement.
Internal files, unlike external files, have no concept of persistent position (between input/output statements). In this regard if you want one read statement to transfer from one record and the next read from another record you will have to reference these records directly.
However, you don't show how you really want to use the input. If you can re-write the input to use a single read statement then the appropriate records will be the source.
For example, if you can rewrite
do i=1,5
read(unit, '(I5)') x(i)
end do
as
read(unit, '(I5,/)') x(1:5)
then you can easily switch to using an internal file.

Exception handling when the file does not exist

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.

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.

Fortran WRITE loop to multiple files?

i want to create new file at each loop, i don't know how to do...
!file1, file2,....., file[n]
OPEN (1,FILE='file1.out',ACCESS='SEQUENTIAL',STATUS='UNKNOWN')
do ph=1,N6
do i=1,nx-1
A(i)=mu*U(i-1)
end do
do j=0,nx
U(j)=A(j)
end do
if (mod(ph,Ne)==0) then ! ?
WRITE(1,200) nt,U(i)
endif
200 format(5E12.4)
end do
Or maybe, i can write with a newline or column ?
I'm a beginner in fortran.
Thanks
Your existing code should output more than just last write statement to the file ... it is a sequential file, which means that the output is added to the file in sequential order. If you are only seeing one output, perhaps that is all that the IF statement is causing to be output?
If you still wish to output to multiple files, the easiest way to output to multiple files is to reuse the unit number and have the program create filenames. You need to close the file / unit and reopen it. This is much more easier than have multiple open statements and unit numbers, which would quickly become awkward as the number of files increased. Here is a code fragment that assumes fewer than 100 files:
do i=1, N
write (filename, '("myfile", I2.2, ".txt")' ) I
open (file=filename,unit=16,...)
calculations...
write (16,'(5E12.4)') nt,U(i)
close (16)
end do