Write in an existing file without overwriting in Fortran - fortran

I have an existing file written by a Fortran program (formatted) and I would like to add few lines at the beginning of the file. The idea is to do so without making a copy of the original file.
I could add a line at the end of the file with:
open(21,file=myfile.dat,status='old',action='write',
form='formatted',position="append")
write(21,*) "a new line"
but when I tried:
open(21,file=myfile.dat,status='old',action='write',
form='formatted',position="rewind")
write(21,*) "a new line"
it overwrites the whole file.
It might be impossible.
At least, I would be glad to have a confirmation that it is effectively impossible.

Yes, it is impossible. With position= you only set the position for writing. Normally you just delete everything past the current record by a write in a sequential file. You may be able to adjust a record at the beginning in a direct access file, but also not just add something at the beginning. You have to make a copy first.

If you are using un-formatted data and know how many lines to expect, try using the direct access file read/write method. This has the potential to store information for each line in a 'record' which can be accessed later much like an array.
In order to append to the beginning, simply create as many empty records as you will have lines in your 'header' at the beginning of the file then go back and change their values to the actual lines you want them to be later.
Example of Direct Access file io:
CHARACTER (20) NAME
INTEGER I
INQUIRE (IOLENGTH = LEN) NAME
OPEN( 1, FILE = 'LIST', STATUS = 'REPLACE', ACCESS = 'DIRECT', &
RECL = LEN )
DO I = 1, 6
READ*, NAME
WRITE (1, REC = I) NAME ! write to the file
END DO
DO I = 1, 6
READ( 1, REC = I ) NAME ! read them back
PRINT*, NAME
END DO
WRITE (1, REC = 3) 'JOKER' ! change the third record
DO I = 1, 6
READ( 1, REC = I ) NAME ! read them back again
PRINT*, NAME
END DO
CLOSE (1)
END
code source, see section on "Direct Access Files":
http://oregonstate.edu/instruct/ch590/lessons/lesson7.html

It is possible !!! Here is a sample program which could accomplish the task.
! Program to write after the end line of a an existing data file
! Written in fortran 90
! Packed with an example
program write_end
implicit none
integer :: length=0,i
! Uncomment the below loop to check example
! A file.dat is created for EXAMPLE defined to have some 10 number of lines
! 'file.dat may be the file under your concern'.
! open (unit = 100, file = 'file.dat')
! do i = 1,10
! write(100,'(i3,a)')i,'th line'
! end do
! close(100)
! The below loop calculates the number of lines in the file 'file.dat'.
open(unit = 110, file = 'file.dat' )
do
read(110,*,end=10)
length= length + 1
end do
10 close(110)
! The number of lines are stored in length and printed.
write(6,'(a,i3)')'number of lines= ', length
! Loop to reach the end of the file.
open (unit= 120,file = 'file.dat')
do i = 1,length
read(120,*)
end do
! Data is being written at the end of the file...
write(120,*)'Written in the last line,:)'
close(120)
end

Related

Reading the same line from a file many times in Fortran

I would like to read the same line of a file many time in Fortran. The concerned data are real values. I tried to build this code as test but I am always getting it wrong.
program advance
implicit none
integer , parameter :: ut = 20
character(len=7) :: fname = 'dat.dat'
integer :: n, idx
character(len=100) :: lnumber
open(unit = ut, file =fname, status='old', action='read')
n = 10
do idx = 1, n
read(ut, '(a)', advance = 'no') lnumber
print *, lnumber
end do
end program advance
The dat.dat file contains one line with 25.325654515464564564
The code return the following error.
At line 13 of file advance.f90 (unit = 20, file = 'dat.dat')
Fortran runtime error: End of record
How do I fix this bug?
Such non-advancing input (using advance='no') doesn't mean that the file position is not advanced at all. It means that the file position isn't advanced beyond what is needed to satisfy the requirements of the input list.
So, in this case, the file position is advanced by reading the single "real number" into the character variable lnumber. The next read will continue from this later point. This later point happens to be the end of the file.
With advancing input more generally, the file position is advanced to the start of the next record even if the record is not required in entirety.
As High Performance Mark comments, reading the same line over and over again likely isn't what you should be doing. You could read the line into a character variable (such as is done here) and repeatedly use that variable as an internal file. However, if you really want to read a line again, consider backspace.

Syntax error in function arguments in fortran

I have a sub routine file as follows
subroutine grids(Ngrids,gridsize,boundx,boundy,boundz,occmatrix,myid)
implicit NONE
integer i,j,k,Ngrids, occmatrix(14,14,10)
integer locx,locy,locz,myid
double precision gridsize,boundx,boundy,boundz
do i = 1, 14
do j = 1, 14
do k = 1, 10
occmatrix(i,j,k) = 0
enddo
enddo
enddo
open (13, file = 'grid_data.9deg')
write(*,'(A,i2)'),' READING GRID FILE ON PROC.....',myid
read(13,*) Ngrids,gridsize
read(13,*) boundx,boundy,boundz
do i = 1, Ngrids
read(13,*) locx, locy, locz
occmatrix(locx,locy,locz) = 1
enddo
close(13)
return
end
It gives the following syntax error in compiling
subroutine grids(Ngrids,gridsize,boundx,boundy,boundz,occmatrix,my
1
Error: Unexpected junk in formal argument list at (1)
It used to run well before
I would believe, your line is to long. Did you add a new argument? Your code looks like free form, but it might be the compiler tried to apply fixed form due to a .f suffix in the filename or something like that. Convince the compiler to assume free formatted source code (by compiler options or usually a .f90 suffix).
Even in free formatted files your line width is limited and you should break longer lines, which would for example look like:
subroutine grids( Ngrids,gridsize,boundx,boundy,boundz, &
& occmatrix,myid )
If you are stuck with fixed format you need to indicate a continuation line by a non blank character in column 6.
Here is how it looks like in fixed form:
subroutine grids(Ngrids,gridsize,boundx,boundy,boundz,
& occmatrix,myid)
Please do not use fixed form anymore! Instead, change your files to end with .f90, most compilers recognize this for free formatted code.

How to open and read multiple files in Fortran 90

I have some issues about opening and reading multiple files. I have to write a code which reads two columns in n files formatted in the same way (they are different only for the values...). Before this, I open another input file and an output file in which I will write my results. I read other questions in this forum (such as this one) and tried to do the same thing, but I receive these errors:
read(fileinp,'(I5)') i-49
1
devstan.f90:20.24:
fileLoop : do i = 50,52
2
Error: Variable 'i' at (1) cannot be redefined inside loop beginning at (2)
and
read(fileinp,'(I5)') i-49
1
Error: Invalid character in name at (1)
My files are numbered from 1 to n and are named 'lin*27-n.dat' (where n is the index starts from 1) and the code is:
program deviation
implicit none
character(len=15) :: filein,fileout,fileinp
integer :: row,i,h
real :: usv,usf,tsv,tsf,diff
write(*,'(2x,''Input file .......''/)')
read(*,'(a12)') filein
write(*,'(2x,''Output file........''/)')
read(*,'(a12)') fileout
open(unit = 30,File=filein)
open(unit = 20,File=fileout)
fileLoop : do i = 50,52
fileinp = 'lin*27-'
read(fileinp,'(I5)') i-49
open(unit = i,File=fileinp)
do row = 1,24
read(30,*) h,usv,tsv
read(i,*) h,usf,tsf
diff = usf - usv
write(20,*) diff
enddo
close(i)
enddo fileLoop
end program deviation
How can I solve it? I am not pro in Fortran, so please don't use difficult language, thanks.
The troublesome line is
read(fileinp,'(I5)') i-49
You surely mean to do a write (as in the example linked): this read statement attempts to read from the variable fileinp rather than writing to it.
That said, simply replacing with write is probably not what you need either. This will ignore the previous line
fileinp = 'lin*27-'
merely setting to, in turn, "1", "2", "3" (with leading blanks). Something like (assuming you intend that * to be there)
write(fileinp, '("lin*27-",I1)') i-49
Note also the use of I1 in the format, rather than I5: one may want to avoid blanks in the filename. [This is suitable when there is exactly one digit; look up Iw.m and I0 when generalizing.]

How to go to the end of the file?

I have opened a file to write a number. I have to write the number at the end of the file so
how to go to the last line to write on it?
You should open the file with
open(..., position="append",...)
Alternatively, you can inquire for the size of the file
inquire(...,size=some_integer_variable,...)
then if the file is a direct access file, you can use this size to calculate the record number of the final record. Alternatively, if the access mode is "stream", you can use
write(..., pos=some_integer_variable)
to write starting at the end of the file.
I've been using the same trick for years, and would be interested in a more elegant way but I can propose you the following method. Note that it is less and less efficient as the file increases in number of lines. Note also that this part of code could endup in an elegant module dedicated to playing with input/output.
Open your file
open(11, file='monfichier')
Compute how many lines there are in your file
nbline = 0 ! should have been declared as an integer
do while(.true.)
read(11,*,iostat=ios) ! ios should have been declared as an integer
if( ios > 0 ) then
stop 'problem somewhere'
else if( ios < 0 ) then ! end of file is reached
exit
else
nbline = nbline + 1
end if
end do
close(11)
at this step, you have the total number of lines stored in variable nbline.
If you want to print something at the Nth line before the last line, then
open(11, file='monfichier')
do i = 1, nbline - N ! see my nota bene at the end of my answer; i and N are integers
read(11,*)
end do
write(11,*)'hello world'
Et voilĂ  !
N.B. : Please be carefull in the way you count for nbline-N or nbline-(N-1), depending on exactly what you want.
subroutine to_last_rec (luout)
! purpose: position to last record of file
implicit none
integer :: luout
logical :: ende
! first executable statement
ende = .FALSE.
do while ( .NOT. ende)
read (luout,*,end=100)
enddo
100 return
end subroutine to_last_rec
PROGRAM example
IMPLICIT NONE
INTEGER :: ierr
OPEN(UNIT=13,FILE="ex.dat")
CALL FSEEK(13, 0, 2, ierr)
! DO WHATEVER YOU WANT THEN
CLOSE(13)
END PROGRAM example
the call to fseek goes to the end of the file ( used like that, check the usage http://docs.oracle.com/cd/E19957-01/805-4942/6j4m3r8ti/index.html)

Fortran: Read one value at a time from a line

I am using FORTRAN to read in data from an ASCII text file. The file contains multiple data values per line but the number of values per line is not constant.
101.5 201.6 21.4 2145.5
45.6 21.2
478.5
...
Normally after a read statement, Fortran would go to the next line. What I want to be able to do is read one data value at a time. If it hits the end of the line, it should just continue reading on the next line. Is this possible?
As pointed out by IRO-bot in their comment to your question, the answer has already been given by M.S.B. Below I have merely provided some code illustrating that answer (as M.S.B.'s post contained none):
program test
character(len=40) :: line
integer :: success, i, indx, prev, beginning
real :: value
open(1,file='test.txt')
do
read(1,'(A)',iostat=success) line
if (success.ne.0) exit
prev = 1
beginning = 1
do i=1,len(line)
! is the current character one out of the desired set? (if you
! have got negative numbers as well, don't forget to add a '-')
indx = index('0123456789.', line(i:i))
! store value when you have reached a blank (or any other
! non-real number character)
if (indx.eq.0 .and. prev.gt.0) then
read(line(beginning:i-1), *) value
print *, value
else if (indx.gt.0 .and. prev.eq.0) then
beginning = i
end if
prev = indx
end do
end do
close(1)
end program test
When running this program using the sample lines you provided the output is
101.5000
201.6000
21.40000
2145.500
45.60000
21.20000
478.5000
I hope you will find this helpful.