Fortran read multi-line record - fortran

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.

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

Related

Can do loops have a label at the beginning

Currently I'm learning Fortran!
Can Do loop have label at the beginning as well as after do keyword as shown in the example??
program sample
implicit integer(a-z)
integer :: b = 20
10 do 15 i = 1, 5
b = b + i
15 end do
if (b .eq. 35) go to 10
print *, b
end program
If not, what is the correct way to use the labels in the do statement(with example program)??
To quote the current standard (F2018 6.2.5):
Any statement that is not part of another statement, and that is not preceded by a semicolon in fixed form, may begin with a statement label
A DO statement like this is not part of another statement: yes it may be labelled.
That said, as you are using it as a go to target, you can consider that there are better ways than labelling the DO statement.
And also, you should try to avoid that 15 label as well. Not only is it redundant (the labelled statement is an end do) but the "labelled DO statement") is obsolescent and may be deleted from the standard. (The "nonblock DO construct", where the labelled statement is something other than end do, was deleted in Fortran 2018, so deletion isn't an empty threat.)
These points together give the program without any labels and with no go to:
implicit none
integer :: b, i
b=20
do
do i = 1, 5
b = b + i
end do
if (b .ne. 35) exit ! or /= instead of .ne.
end do
print *, b
end
(I've also used implicit none and used an assignment for b instead of initialization.)
In general, while you can put labels in many places, there are very, very few places where you should use the labels.
"Part of another statement" makes lines like the following line bad:
if (.true.) 10 print *, "Hello"
The PRINT statement here is an action statement which is part of the IF statement. While the IF statement as a whole may be labelled, the PRINT statement is part of that statement and so may not be labelled.

How to read large amount of data from files and store them in variable?

I have a file which has values of pressure at every element. I require the element number (ELNO) and pressure (PLOAD) and pass through a subroutine. I am not able to read them separately from the file.
The data would read like given below starting with S175..
I want to be able to read this file, say A0001.txt, and read the lines one-by-one. When reading the lines I want to store the number after the first dot, for eg 1007, 1008 etc into a variable ELNO and the number after the last comma in a variable PLOAD in a loop. This is because I will require each value of ELNO and check a condition with the IF loop.
My problem is reading the file and storing in a variable like ELNO(i) something like that. The name S175 is constant.
I think I understand the logic. I have to store each like as a string and start iterating from the 6th position in the string till it finds the first "," and store that in ELNO(i). But I am new to FORTRAN and not able to get it. I have been trying for the past week learning FORTRAN to do this. But, not able to do this problem.
I tried a code like this below but its not reading line by line since I did not put it under a loop I guess.
S175.1007,P,0.221948
S175.1008,P,0.221943
S175.1009,P,0.221929
S175.1010,P,0.222287
S175.1018,P,0.222438
S175.1019,P,0.222425
.....
.....
.....
.....
.....
S175.13000,P,-1990
S175.13001,P,-1980
S175.13002,P,-2009
PROGRAM BARGE
implicit none
CHARACTER X*80
OPEN(UNIT=60, FILE="A0001.txt", ACCESS='SEQUENTIAL', ACTION='READ')
READ(UNIT=60, FMT='(A)', END=10)X
10 OPEN(UNIT=61, FILE="2.txt", ACTION="write")
WRITE (UNIT=61,FMT='(A)')X
END PROGRAM BARGE
Thank you everyone. I have completed it myself. There might be an easier and faster method but this works fine. Let me know if there is a more efficient method. I could learn. :)
PROGRAM BARGE
implicit none
CHARACTER PRES*80
INTEGER :: SUCCESS
INTEGER :: K, L, M, ELNO ! K is for the element number,L is word P and M is for pressure value
REAL :: PLOAD
OPEN(UNIT=60, FILE="1.txt", ACCESS='SEQUENTIAL', ACTION='READ')
DO
READ(UNIT=60, FMT='(A)', IOSTAT=SUCCESS)PRES
IF (SUCCESS.NE.0) EXIT
K=6
DO WHILE (PRES(K:K) .NE. ',')
K=K+1
END DO
READ(PRES(6:K-1), *) ELNO
PRINT *, ELNO ! ELEMENT NUMBER
L=K+1
DO WHILE (PRES(L:L) .NE. ',')
L=L+1
END DO
M=L+1
DO WHILE (PRES(M:M) .NE. ' ')
M=M+1
END DO
READ(PRES(L+1:M-1), *) PLOAD ! PRESSURE ON THE ELEMENT
PRINT *, PLOAD
OPEN(UNIT=61, FILE="2.txt", ACTION="write")
WRITE (UNIT=61,FMT='(A)')PRES
END DO
READ (*,*) PRES
END PROGRAM BARGE
It would be easy to use Fortran's list-directed input to read the data items from a line such as S175.1019,P,0.222425. The following snippet can replace the do loop in OP's own answer:
DO
READ(UNIT=60, FMT='(A)', IOSTAT=SUCCESS) PRES
IF (SUCCESS.NE.0) EXIT
READ(PRES(6:),*) ELNO, P, PLOAD
WRITE(*,*) ELNO, PLOAD
END DO
For this to work you have to include a declaration such as
CHARACTER(LEN=1) :: P
to catch the letter P in each line of the input file. The important line is this one
READ(PRES(6:),*) ELNO, P, PLOAD
which uses the edit descriptor * which tells the compiler / run-time to figure out how to read values for the three variables (one integer, one character, one real) from the 6th and following characters in the line PRES.
Fortunately, with a nice clean input file such as the one shown the compiler has no trouble reading the values, all the scanning for occurrences of , is unnecessary. If, for another application, you ever need to search a line for occurrences of a character use the intrinsic function index.

Fortran Print Statement formatting, too many white spaces

The Fortran program I am writing is to add two integers together and get a sum, I have to print it out as "4+5=9", whereas 4 and 5 are user inputs. I ran into some problems of printing out too many unnecessary spaces in the print statement. So I did some researches and then I found a solution. But it keeps telling me the syntax is wrong in the PRINT statement and I have no idea why and how. Please help me with this situation. Thank you
I used the solution provided by this link: Output formatting: too much whitespace in gfortran
PROGRAM SumProgram
IMPLICIT NONE
!Define and initialize variables
integer :: IntegerOne, IntegerTwo, Sum
IntegerOne = 0
IntegerTwo = 0
Sum = 0
!Prompt the user for inputs
print *,' Enter the two Integers to be added together: '
READ(*,*) IntegerOne, IntegerTwo
!Do the Calculation
Sum = IntegerOne + IntegerTwo
PRINT (*, '(I0, "+", I0, "=", I0)') IntegerOne, IntegerTwo, Sum
END PROGRAM
I am expecting the output of "4+5=9"
Your syntax is good for the WRITE statement, but PRINT is like
PRINT '(I0, "+", I0, "=", I0)', IntegerOne, IntegerTwo, Sum
but I find
PRINT '(5g0)', IntegerOne, "+", IntegerTwo, "=", Sum
easier to read. (If you can't use g0, use '(i0,a,i0,a,i0)'.)
Or just exchange the PRINT for WRITE... I personally always use write and only use print for temporary debugging messages. It makes them easy to find.
Remember that the basic forms are
PRINT *, "Hello"
vs.
WRITE(*,*) "Hello"
so the forms with explicit format must conform to them.

Is it possible to limit recursive reading in one line?

Further question from my last post: What is this error in fortran and how to stop the program when it occurs?
In my last post, I want to figure out how to identify the reading error when there is not enough elements in the input file which can be solved by using an iostat in the read statement.
My new code is:
program main
implicit none
integer ioerr, switch_1(3), switch_2, i, readerr
open(100, FILE='./input_error.gr', ACTION='READ', IOSTAT=ioerr)
if (ioerr == 0) then
read(100, *, iostat=readerr) (switch_1(i), i=1,3)
if(readerr .ne. 0) then
write(*,*) 'switch 1 wrong'
stop
end if
write(*,*) 'Switch_1 is: ', switch_1
read(100,*,iostat=readerr) switch_2
if(readerr .ne. 0) then
write(*,*) 'switch 2 wrong'
stop
end if
write(*,*) 'Switch_2 is: ', switch_2
else
write(*,*) 'File not read'
end if
end program main
my input file looks like this:
1,2,3
1
My new question is when there is less than three elements in the first line, the program will automatically read elements in the next line for Switch_1 and Switch_2 will have nothing to read and as the program is coded, it will return "Switch 2 wrong".
For example, when the input is like this:
1,3
2
However, what is really wrong is switch_1. Since there is not enough elements for switch_1, it goes to the next line the fill the last position of the array. This is not what I want. What I want is limit the reading in one line so the program will not read another line automatically, and in this case the program can stop at switch_1 and give me 'Switch 1 wrong. Is it possible?
I typically solve this problem by reading the line into a string first, and then attempting to read numbers from the string. For example,
character(len=200) :: str ! Long enough to hold a whole line
integer :: i(3), ier, fid
fid = 100
open(fid, FILE='./input_error.gr', ACTION='READ')
! -- Read a *single* line, and put it all into str
read(fid,'(a)') str
! -- Read str into integer array i
read(str,*,iostat=ier) i(1:3)
! -- Check if the line actually contained 3 integers
if (ier /= 0) then
write(*,*) 'Unsuccessful read'
endif
The read statement is therefore only able to use the current line because that is all str contains.
I figure out that if I add a comment at the end of each line, since character a is different data type, it will not be read as integer numbers. The advantage is when there is not enough elements for each switches, the program will read the comment first instead of reading the next line and give the right error message.
For example, make the input like:
1,2,3 ! Switch 1
4 ! Switch 2
with this structure, even if there is not enough input elements for switch 1, it is not reading 4 as the third element, instead, it is reading the comment, "! Switch 1" which will definitely return an error for Switch 1. This is exactly what I want and this works fine with number reading procedures. However, if the data type of switch_1 is character in the beginning, what will happen? It will not stop at numbers.

How do I write a newline character in a character variable in Fortran?

I want to store newline character in my character buffer variable buff. So far my code goes like this:
program TYPE_CHECK
c newline storage in buffer
character(100), dimension(10) :: buff
integer, dimension(10) :: x
integer :: i
do i=1,10
x(i) = i
enddo
do j=1,10
write(buff(j), 1) x(j), x(j)
1 format(' This is line ', I3, /,
* ' This is newline ', I3)
enddo
do j=1,10
write(*, "(A100)") buff(j)
enddo
end program TYPE_CHECK
This gives the following error:
At line 13 of file myfoo6.F
Fortran runtime error: End of file
You can use the NEW_LINE intrinsic function to query the character used to represent newline in formatted stream output. This character typically does what you want when you send it to the console. It is probably easiest to write it out using a separate character data descriptor in your format specification.
Using a slash edit descriptor is actually a request to progress to the next record. For an internal file (writing to a character variable), this is the next array element. In your example code you are passing a scalar as the character variable (so just one record), hence an end of file condition occurs.