Trouble with if/then statements containing character variables in Fortran 77 - if-statement

I am currently trying to write a program in Fortran 77 that calculates the number of words in a text file. The text files looks like this:
Hello world. Hello world. Hello world. Hello world. Hello world. Hello world. Hello world. Hello world. Hello world. Hello world. Hello world. Hello world. Hello world.
My program currently looks like this:
program COUNT
implicit none
character text*100000
integer i, a, nw
nw=1 !number of words
open(9, FILE='file.txt', STATUS='old')
read(9, '(A)') text
a=0
10 do i=1, LEN_TRIM(text)
if (text(i:i) .ne. " ") then
if (a .eq. 0) then
goto 10
else
a=0
nw=nw+1
goto 10
endif
else
if (a .eq. 0) then
a=a+1
goto 10
else
goto 10
endif
endif
enddo
print *, "Number of words: ", nw
end
I did this on paper and it should work, however, my program is getting stuck in the do-loop. I think that this might have something to do with this statement:
if (text(i:i) .ne. " ") then
Am I allowed to write the logical expression this way? If not, does anyone have any hints on how I could rewrite this code? I'm sorry if my program is a bit messy; I'm relatively new at this. I'm trying to improve my coding skills to do computational chemistry. Thanks so much for the help!

All your if branches go to goto 10 which points at the beginning of the loop. There is no way to end because this will restart the loop from the beginning. Do not use goto to start new iteration end do s meant for that.

Related

Compile error for a simple Fortran 77 program

I copied and pasted in Sublime Text the following program from a Fortran 77 tutorial:
program circle
real r, area
c This program reads a real number r and prints
c the area of a circle with radius r.
write (*,*) 'Give radius r:'
read (*,*) r
area = 3.14159*r*r
write (*,*) 'Area = ', area
stop
end
I saved it as circle.f and compiled from the Terminal (macOS Sierra):
gfortran circle.f
It returned the error message:
circle.f:1:1:
program circle
1
Error: Non-numeric character in statement label at (1)
circle.f:1:1:
program circle
1
Error: Unclassifiable statement at (1)
How can I fix it? (The answer for another similar question does not solve the problem.)
Fortran 77 has fixed form source. Only characters between the 7th and the 73rd column can be used for statements. (The first 6 characters are used to declare the whole line a comment, as numeric labels, or to denote this line to be a continuation of the previous.) The 74th and later characters are simply ignored.
Inside this range, spaces are ignored. So the following lines would be identical:
column 1 1 2 2 3 3 4 4
1 5 0 5 0 5 0 5 0 5
-----------------------------------------------
if (i .le. 10) call my_sub(i)
if(i.le.10)callmy_sub(i)
i f ( i. le .10) cal lmy_ sub(i)
I leave it up to you to decide which one is easiest to read.
But if you start at the first character, even with the starting "program" statement, the compiler will complain. It expected a c, C, ! (to declare the whole line a comment) or a digit as the beginning of a numeric label.

How to read the lines of the input in arbitrary order?

I would like to ask how I can read the lines of the input in arbitrary order. In other words: how to read a given line of the input? I have written the next test program:
program main
implicit integer*4(i-n)
dimension ind(6)
do i=1,6
ind(i)=6-i
end do
open(7,file='test.inp',status='old')
do i=0,5
call fseek(7,ind(i+1),0)
read(7,*) m
write(*,*) m
call fseek(7,0,0)
end do
end
where test.inp contains:
1
2
3
4
5
6
My output given is:
4
5
6
2
3
4
What is the problem? I would expect
6
5
4
3
2
1
for a text file the simplest thing is to just use an empty read to advance lines. This will read the nth line of file opened with unit=iu
rewind(iu)
do i=1,n-1
read(iu,*)
enddo
read(iu,*)data
Note if you are doing a bunch of reads from the same file you should consider reading the whole file into a character array, then you can very simply access lines by index.
here is an example of reading in a whole file:
implicit none
integer::iu=20,i,n,io
character(len=:),allocatable::line(:)
real::x,y
open(iu,file='filename')
n=0
do while(.true.) ! pass through once to count the lines
read(iu,*,iostat=io)
if(io.ne.0)exit
n=n+1
enddo
write(*,*)'lines in file=',n
!allocate the character array. Here I'm hard coding a max line length
!of 130 characters (that can be fixed if its a problem.)
allocate(character(130)::line(n))
rewind(iu)
!read in entire file
do i=1,n
read(iu,'(a)')line(i)
enddo
!now we can random access the lines using internal reads:
read(line(55),*)x,y
! ( obviously use whatever format you need on the read )
write(*,*)x,y
end
One obvious drawback to this is you can not read data that spans multiple lines the same as if you were reading from the file.
Edit: my old version of gfortran doensn't like that allocatable character syntax.
This works:
character(len=130),allocatable::line(:)
...
allocate(line(n))

How to skip a data line in fortran [duplicate]

This question already has answers here:
Skip a line from text file in Fortran90
(3 answers)
Closed 7 years ago.
enter image description here
I have this data file
and want to read this data without line 1,2,3,4,5
program example
real data(15,9)
OPEN ( unit=10, file='filename' )
do i = 1, 15
READ (10, *) (data(i,j), j=1,10)
enddo
print *, data(4,1), data(4,2), data(4,3)
stop
end
this is my fortran code.
how can i change this code
Looking something like this?
input file: data
line1
line2
line3
line4
line5
line6
line7
line8
line9
line10
fortran code:
implicit none
integer:: lskip,lread
character(len=20)::line
open(20, file = "data")
!skip first 5 line
do lskip = 1,5
read(20,*)
End do
! First 5 lines skiped
! Now read actual lines
do lread = 1,5
read(20,*)LINE
write(*,*)line
End do
close(20)
end
Result
$gfortran so.f90
$./a.out
line6
line7
line8
line9
line10
NB: This is a minimal example, just for showing the skipping. You will change the read inside lread loop to actually read your file according to your data format
One way to do this is to put in a READ statement for each line that you want to "skip". Each time a READ statement is encountered, it reads in the data and then moves the "pointer" in the file down to the next line. So, for example, to skip 3 lines of header information:
DO 50 ilines = 1,3
READ(1,*)
50 continue
This in effect READs and stores nothing, but moves the pointer in the file forward 3 lines.

Starting reading from specific line numbers in Fortran

I have a file with 1000s of numbers like:
0000
0032
1201
: :
: :
: :
2324
Depending on an input parameter "n", I want to read "m" numbers from this file from line numbers "n" to "n+m-1".
Any ideas how can I do this in Fortran?
I don't know if you have tried it yourself, but here is an minimal example:
say, your input file looks like this:
0000
0032
1201
1234
4567
7890
2324
use this code (after reading it)
Program jhp
Implicit None
integer :: i
integer, parameter :: &
m=7, & !total number of line
n=4, & !line to skip
p=3 !lines to read
integer,dimension(m)::arr !file to read
open(12,file='file_so',status='old')
do i=1,n
read(12,*)arr(i)
end do
do i=1,p
read(12,*)arr(i)
write(*,*)arr(i)
end do
End Program jhp
This skips first n line, and reads p lines after that.
Hope that helps
may be,
open (unit, file ...)
do i=1,n
read(unit,*) crap
end do
do i =n,n+m-1
read(unit,*) whatever
end do
close(unit)
is what you are looking for. this is untasted, but may give you a go.
edit: direct access is better for this type of job:
Just realised, though this is the easiest one, not the preferred one.
You can open the file in direct access mode and complete your job as:
OPEN( unit, file, ACCESS='DIRECT', RECL=100, FORM='FORMATTED')
READ( unit, *, REC=n, ERR=10 ) x

FORTRAN looping lines and character positions?

I'm trying to loop through all the lines in a document using FORTRAN 77 and comparing particular line positions to strings and then editing it.
E.g.:
|BXK |00640.3A |AWP |1.01|
|BUCKEYE MUNICIPAL AIRPORT |08794|
I want to change the 08794 to 0871994 in the second line.
This is what I have so far:
PROGRAM CONVERSION
IMPLICIT NONE
CHARACTER(LEN=120) :: ROW
CHARACTER(LEN=2) :: DATE1='19', DATE2='20'
INTEGER :: DATENUMBER
INTEGER :: J
OPEN(UNIT=1, FILE='BXK__96B.TXT', STATUS ='OLD')
OPEN(UNIT=2, FILE='BXK__96B_MODIFIED.TXT', STATUS='UKNOWN')
DO J=1,10000
READ(1,'(A)') ROW
IF (J==2) THEN
DATENUMBER = ICHAR(ROW(76))
IF ((DATENUMBER.LE.9) .AND. (DATENUMBER.GE.2)) THEN
WRITE(2, '(A)' ROW(1:75), DATE1, ROW(76:120))
ELSE
WRITE(2, '(A)' ROW(1:75), DATE2, ROW(76:120))
ENDIF
END IF
END DO
CONTINUE
CLOSE(1)
CLOSE(2)
END
Ahh, so what you mean is, you want to convert the 2-digit representation of the year found at the right end of line 2 into its 4-digit representation. You seem already to have figured out how to find the position of the leading digit of the year, ie 76. Rather easier than what you have written would be
integer :: year
.
.
.
read(line(76:77),'(i2)') year ! this reads year from the characters in positions 76,77
if (20<=year.and.year<=90) then ! not sure if this precisely your test
year = year+1900
else
year = year+2000
end if
write(line(76:79),'(i4)') year
I haven't gone to the trouble of integrating this into the rest of your code, that should be straightforward, if not ask for more help.
Actually, I suppose you probably haven't figured out how to find the column at which you want to start reading the year from line 2. Precisely how you do this depends on what the format of your file really is. The functions you need to familiarise yourself with are, as one of the comments tells you INDEX and SCAN.
If you are looking for the 4th character after the 2nd occurrence of | in line 2 you could do it this way:
integer :: posn_of_2nd_vertical_bar
.
.
.
posn_of_2nd_vertical_bar = scan(row(scan(row,'|')+1:),'|')
and then replace your constant 76 with posn_of_2nd_vertical_bar+4