New to Fortran, questions about writing to file - fortran

I am completely new to Fortran and pretty new to programming in general. I am trying to compile a script someone else has written. This is giving me a few problems. The top half of the code is:
C
C Open direct-access output file ('JPLEPH')
C
OPEN ( UNIT = 12,
. FILE = 'JPLEPH',
. ACCESS = 'DIRECT',
. FORM = 'UNFORMATTED',
. RECL = IRECSZ,
. STATUS = 'NEW' )
C
C Read and write the ephemeris data records (GROUP 1070).
C
CALL NXTGRP ( HEADER )
IF ( HEADER .NE. 'GROUP 1070' ) CALL ERRPRT(1070,'NOT HEADER')
NROUT = 0
IN = 0
OUT = 0
1 READ(*,'(2i6)')NRW,NCOEFF
if(NRW .EQ. 0) GO TO 1
READ (*,'(3D26.18)',IOSTAT =IN) (DB(K),K=1,NCOEFF)
DO WHILE ( ( IN .EQ. 0 )
. .AND. ( DB(2) .LT. T2) )
IF ( 2*NCOEFF .NE. KSIZE ) THEN
CALL ERRPRT(NCOEFF,' 2*NCOEFF not equal to KSIZE')
ENDIF
C
C Skip this data block if the end of the interval is less
C than the specified start time or if the it does not begin
C where the previous block ended.
C
IF ( (DB(2) .GE. T1) .AND. (DB(1) .GE. DB2Z) ) THEN
IF ( FIRST ) THEN
C
C Don't worry about the intervals overlapping
C or abutting if this is the first applicable
C interval.
C
DB2Z = DB(1)
FIRST = .FALSE.
ENDIF
IF (DB(1) .NE. DB2Z ) THEN
C
C Beginning of current interval is past the end
C of the previous one.
CALL ERRPRT (NRW, 'Records do not overlap or abut')
ENDIF
DB2Z = DB(2)
NROUT = NROUT + 1
print*,'Out =', OUT
WRITE (12,REC=NROUT+2,IOSTAT=OUT) (DB(K),K=1,NCOEFF)
print*,'Out2 =', OUT
IF ( OUT .NE. 0 ) THEN
CALL ERRPRT (NROUT,
. 'th record not written because of error')
ENDIF
So, when I print "Out" and "Out2" to the screen I find that Out=0 and Out2=110. As it is not longer equal to zero, the program gives me an error. Therefore I am basically wondering about what is happening here:
WRITE (12,REC=NROUT+2,IOSTAT=OUT) (DB(K),K=1,NCOEFF)
I assume that 12 refers to the file I have opened (and created), and want to write to. What does the rest of the first brackets do? And what is the point of the second? Does that gives me the information I want to put in my file? In case, where does DB get filled with that?
Generally I am wondering what is going wrong? Why does OUT change value? (I need t
NCOEFF is defined as an Integer in the beginning of the programme, and DB: as DOUBLE PRECISION DB(3000), DB2Z/0.d0/ , so I assume DB is an array of some sort.

In this program, OUT is telling if the write statement was successful or not. (the IOSTAT parameter to the write statement means "I/O status", or input/output status). It returns 0 if the I/O operation was a success, or the number of the error code otherwise. You can find what the error codes mean here.
I'm not familiar with the REC parameter, but a starting place to investigate yourself can be found here.

To quote the handbook REC indicates the record number to be read or written. As advised, see the documentation which accompanies your compiler for further explanation.
(DB(K),K=1,NCOEFF) means 'all the elements in DB from 1 to NCOEFF. You are looking at an io-implied-do statement.

The statement
WRITE (12,REC=NROUT+2,IOSTAT=OUT) (DB(K),K=1,NCOEFF)
is, if memory serves me, called an "implied DO-loop". As written, it will write NCOEFF values from array DB, starting at DB(1).
It is called an implied DO-loop because the explicit form would be (in FORTRAN IV, for the ancients: I know it a lot better than the more modern variations) something along the lines of:
DO 10 K=1,NCOEFF
WRITE (12,REC=NROUT+2,IOSTAT=OUT) DB(K)
10 CONTINUE
(Pretend that the first two lines are indented six columns.) This is a DO-loop. The implied DO-loop form lets you put the "loop" right in the input/output statement.
What makes it useful is that you can have multiple arrays, and multiple loops. For a simple example:
WRITE (12,REC=NROUT+2,IOSTAT=OUT) (DB(K), DC(K), K=1,NCOEFF)

110 is the error code thrown by the WRITE call. You need to check your FORTRAN RTL (run-time library) reference. It should list the possible error codes. I think 110 means that you're trying to convert a double-precision value to an integer, but the value is bigger than you can store in an integer. Maybe dump the values in DB and see.

Related

FORTRAN code with ascii data

I have a data in ASCII format (.txt file) in which date is given in a column in format yearmonthday (i.e.19900601). I want to separate this column into three columns with year, month and date in each column. Can anyone tell how to do this in Fortran? My data file and code is as following:
datum nied
19480501 -1
19480502 -1
19480503 2
19480504 -1
19480505 2
19480506 -1
19480507 -1
19480508 -1
19480509 -1
19480510 -1
19480511 -1
19480512 2
. .
. .
Code:
program ascii_read
!real(kind=8):: rain(np)
real,allocatable:: rain(:)
integer::np=15739
!integer(kind=8)::day(np)
integer,allocatable::day(:)
character(len = 80)::firstline
integer::i,j
integer,allocatable:: year(:)
allocate (year(np-1))
allocate (rain(np))
allocate (day(np))
open(1243,file="11700.text",status="unknown")
open(12,file="11700_output.text",status="unknown")
read(1243,*)firstline
do i=2,np
read(1243,1111)day(i),rain(i)
end do
1111 Format(i6,F5.2)
write(*,*)day
do j = 1,np-1
year(j)=day(j)
end do
write(*,fmt='(i4)')year
1 format(I4)
!write(*,*)year
return
stop
end program
This gives only year separate in a column,NOT month and day. Any idea how to separate month and day from this data file?
you can use a formatted read to explicitly pull out each of the fields:
integer year,month,day,rain
...
read(1234,'(i4,i2,i2,i3)')year,month,day,rain
In your code you use i6 so day(i) holds things like '194805', then rain(i) is read from the remainder of the line (ie. the last two digits of the "date" integer a space and another integer). I don't know what the f5.2 format does with that but it can't be what you want)
You have to analyse the relationship between your input and the output that you want and then implement the relationship; that is how programming works. You have to first know a method to solve the problem by yourself and then teach the computer how to do it.
For this problem, you can simply see that the first 4 digits represent the year, the next two the month and the last 2 the date. To get the first 4, you divide the full number by 10000, it simply reject the last 4 (month and day). You use the modulo operation to get the last four. And do the same to extract the month from the last two.
Define new array variables month and date and allocate them to the same size as day, also add a new integer variable tmp and change your second loop to this:
do j = 1,np-1
year(j)=day(j)/10000
tmp = mod(day(j), 10000)
month(j) = tmp/100
date(j) = mod(tmp,100)
end do
I will also advise you to use free formatting for reading. You can use fixed format for writing to align data and make it easy to visualize.
Go for modern programming when you start. Litteral numbers are not a good idea in a code, so use named constants for file ids. Make sure that you close files when you do not need them anymore. When you are openning file for reading, use status='old', you want the file to be there or you want you program to stop with an appropriate message. When you are using format, use the format parameter of read and write instead of format statement, for example with the name arg fmt as you did at some places. This make it easy to debug. So your program could look like this.
program ascii_read
!real(kind=8):: rain(np)
integer, parameter :: inputId = 1243
integer, parameter :: outputId = 12
real,allocatable,dimension(:):: rain
integer::np=12
!integer(kind=8)::day(np)
character(len = 80)::firstline
integer::i,j, tmp
integer,allocatable,dimension(:):: day, year, month, date
allocate ( year(np-1), rain(np), day(np), month(np), date(np) )
open(inputId,file="11700.text",status="old")
open(outputId,file="11700_output.text",status="unknown")
read(inputId,*)firstline
do i=2,np
read(inputId,*)day(i),rain(i)
end do
close(inputId)
write(*,*) day
do j = 1,np-1
year(j)=day(j)/10000
tmp = mod(day(j), 10000)
month(j) = tmp/100
date(j) = mod(tmp,100)
! just to see what we get.
write(*, *) day(j), year(j), month(j), date(j)
end do
!write(*,fmt='(i4)')year
!1 format(I4)
!write(*,*)year
return
stop
end program
Thank to IanH for the comment, latest version of fortran included a newunit option that takes care of the IO unit number for programmers. This frees you from defining named constant for unit number. If you do not work with a latest version (Some companies do not upgrade often), there is one ready for use in fortranwiki.
A "hybrid" approach of the other two answers is to first read in the data into a buffer and then split it into integers
character(50) buf
integer year, month, day, rain
read( 10, * ) buf, rain
read( buf, "(i4,i2,i2)" ) year, month, day
! or equivalently
! read( buf(1:4), * ) year
! read( buf(5:6), * ) month
! read( buf(7:8), * ) day
Here, list-directed I/O is used to skip possible spaces before the first column, while integers are extracted based on widths. Also, comment lines starting with "#" (if any) can be skipped by inserting if ( buf(1:1) == "#" ) cycle after the first read statement, for example.

Fortran Print Line Number While Reading Input File

I am receiving a formatting error from an input file, and I would like to determine where the formatting error is occurring in the input file.
My question is: Is there a way to print the line number of the input file where the error is occurring in my fortran code?
This is the error I get:
fmt: read unexpected character
apparent state: unit 4 named input_file
last format: (6(I3,X,F7.2,X,I2,X))
lately reading sequential formatted external IO
Which means that the code is getting hung up on some line in my input file that doesn't correspond with the above format.
Here is the part of my code where Fortran is reading in the input file:
DO 20 K = 1,NUMB
READ(4,200) (NSTN(J),TT(J),IKPS(J),J=1,6)
DO 30 J = 1,6
L = L+1
ISTO(L,N) = NSTN(J)
SECT(L,N) = TT(J)
KWV(L,N) = IKPS(J)
30 CONTINUE
20 CONTINUE
KOBS(N) = NSTM
10 CONTINUE
100 FORMAT(5(I2,X),F6.2,X,F5.2,X,F7.3,X,F6.3,X,F8.3,X,F6.3,
& X,F6.2,X,F5.2,X,I3,X,F4.1,X,F5.2,X,F7.3)
200 FORMAT(6(I3,X,F7.2,X,I2,X))
RETURN
END
I'd like to add a line in the above piece of code to identify the current line the code is reading, so that when it gets hung up, I'll know which line contains the error. Thank you for your help.
Here's what I tried and it's giving me another error:
c READ(4,200) (NSTN(J),TT(J),IKPS(J),J=1,6)
READ(4,200)line
READ(line,*,iostat=ios) (NSTN(J),TT(J),IKPS(J),J=1,6)
IF(ios>0)THEN
WRITE(*,*)'Error Reading Line',line
STOP
ENDIF
INTEGER ios
CHARACTER*(200)line
With a read statement like
READ(4,200) (NSTN(J),TT(J),IKPS(J),J=1,6)
an error in the input results in (error) termination of the program. One has no control over this termination, and in particular one can't do further processing.
There are two ways to avoid this termination, and both involve using an additional specifier in the read statement. One is iostat= and the other err=. If either of these is present then an error doesn't result in termination.
With iostat (for integer istat):
READ(4,200,iostat=istat) (NSTN(J),TT(J),IKPS(J),J=1,6)
then on an error condition, istat will have a (processor-dependent) positive value. It will be zero when (and only when) there is no error.
With err (for some label, say, 991):
READ(4,200,err=991) (NSTN(J),TT(J),IKPS(J),J=1,6)
Putting all that together, let's imagine an outer loop
DO 100 LINE=1,91959
READ(4,200,IOSTAT=ISTAT) (NSTN(J),TT(J),IKPS(J),J=1,6)
IF (ISTAT.NE.0) THEN
PRINT *, 'It went wrong on line', LINE
STOP
END IF
...
100 CONTINUE
or
DO 100 LINE=1,91959
READ(4,200,ERR=991) (NSTN(J),TT(J),IKPS(J),J=1,6)
...
100 CONTINUE
...
991 PRINT *, 'It went wrong on line', LINE
STOP
[I couldn't bring myself to write that code like it really was 1980.]
Add an explicit loop to read your data like:
DO 20 J = 1,6
write (*,*) 'Reading line = ', J
READ(4,100) NSTN(J),TT(J),IKPS(J)
20 CONTINUE
100 FORMAT(I3,X,F7.2,X,I2,X)
This way, you will know exactly where it stopped thank to the write statement in the reading loop. Note that I added two new labels, 20 to control the new loop and 100 for the new format statement. Adapt accordingly.
==============
DO 20 K = 1,NUMB
WRITE (*,*) 'Reading line = ', K
READ(4,200) (NSTN(J),TT(J),IKPS(J),J=1,6)
DO 30 J = 1,6
L = L+1
ISTO(L,N) = NSTN(J)
SECT(L,N) = TT(J)
KWV(L,N) = IKPS(J)
30 CONTINUE
20 CONTINUE
KOBS(N) = NSTM
200 FORMAT(6(I3,X,F7.2,X,I2,X))
RETURN
END

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.