Fortran read multi-line record
-
05-06-2021 - |
Question
I have an old Fortran program for reading records from text files. The records are of the form:
record_type field1 field2 ... fieldN ;
These records might spread over several lines, the character ; shows that the record has finished. The old code is:
2 read(data,"(a130)",end=3)line
shift=adrec(nbrec)-1
read(line,*,end=1)typrec(nbrec),(field(shift+i),i=1,65)
1 do
do j=shift+1,shift+i-1
k=index(field(j),';')
if(k .ne. 0)then
if(k .eq. 1)then
adrec(nbrec+1)=j
else
field(j)(k:)=''
adrec(nbrec+1)=j+1
endif
go to 2
endif
endo
read(data,"(a130)")line
read(line,*,end=1)(field(shift+i),i=1,65)
enddo
3 continue
This code works with Intel Compiler (ifort) as it is. With gfortran it fails! The first problem is that the implicit read on the third line, with ifort, gives i to be equal to the number of fields actually read, while in gfortran it always gives 65. The second problem is that in the same implicit read, with ifort, the character ; is read as a normal field but with gfortran is skipped.
Anyone can help me solve these two problems? Any other ideas for replacing the code all together are also welcome.
Solution
Does this work? Aside from do while
(and exit
control structures...), this is f77 compliant (I think). The do whiles could relatively easily be replaced by goto/continue if necessary. I don't know what type you ultimately want the strings to be, so I return strings (and assume a field can't be longer than 24 characters)...I also assume a "line" can't be longer than 1024 characters.
Sorry about the lack of comments, but the function names are descriptive enough I think. Generally, I think it is a good idea to use functions/subroutines when programming as that makes the code much more readible...
program main
character*1024 line
integer stat,stat2,i,nf
character*24 fld
character*16 fmt
open(unit=21,file='test.dat',status='old')
stat=0
do while(stat.eq.0)
call readline(21,line,stat)
stat2=0
i=1
do while(.true.)
call getfield(line,fld,stat2)
if(stat2.ne.0) exit
i=i+1
write(*,*) i,stat2,fld
enddo
write(*,*) " "
enddo
end
subroutine getfield(line,field,stat)
integer l,i,stat
character*(*) line,field
!Get first field and shift the line
l=1
if(line.eq.' ')then
stat=1
else
stat=0
endif
do while (line(l:l).eq.' ')
l=l+1
enddo
i=l
do while (line(i:i).ne.' ')
i=i+1
enddo
if((line(l:l).eq.'"').or.(line(l:l).eq."'"))then
do while(line(i:i).ne.line(l:l))
i=i+1
enddo
endif
field=line(l:i)
line=line(i+1:)
return
end
subroutine readline(unit,line,stat)
integer unit
character*(*) line
integer stat,i
!read one "line" Lines are sequences seperated by ';' (can have newlines in there)
stat=0
i=1
do while (.true.)
read(unit,'(A)',end=100) line(i:)
i=index(line,';')
if(i.eq.0)then
i=len_trim(line)+2
else
line(i:)=' ' !remove the trailing semicolon.
exit
endif
enddo
return
100 continue
stat=1
return
end