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.
Related
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.
I want to add blank space in a string. For example: name is a variable that equal to "abcxyzdefg".
Now, I want to print this string as: abc xyz defg
I used this program
program name_space
implicit none
character(len=30) :: name = "abcxyzdefg"
write(*,3) name
3 format (A3, 2X, A3, 2X, A4)
end program
I want output as:
abc xyz defg
But, I am getting by this way:
abc
You must refer to the appropriate substrings
write(*,3) name(1:3), name(4:6), name(7:)
with just doing write(*,'(A3,1X,A3,...)') name the first descriptor prints the first three characters of name and then the output list is finished, there are no more items to be printed, so the write statement terminates.
With the output statement
write(*,3) name
we are treating name as a single transfer item, processed by the A3 format. This A3 format prints the first three characters for the string.
There's no further processing that can be done which transforms the item to a desirable form.
Instead, we may want to have three different transfer items. One way is as in the answer by Vladimir F, to use individual substrings:
write(*,3) name(1:3), name(4:6), name(7:10)
write(*,3) (name(i:i+2), i=1,4,3), name(7:) ! Can use implied do if desired
We can also split name in some way to get an array (if the elements are the same length that we know). As an array, each element forms an individual output item:
write(*,3) TRANSFER(name, 'aaa', 3)
write(*,'(3A4)') split_into_chunks_of_4(name)
or we can transform the string to add spaces and then output that:
write(*,'(A)') split_into_chunks(name) ! for some suitable function
For the case of the question, Vladimir F's answer is the best approach. In other cases there are many options.
As the more general case of inserting spaces into a string hides much of the hard work, it's perhaps only fair to give an indication of an approach:
! Add spaces to a string str at the breakpoints bps
function split(str, bps)
character(*), intent(in) :: str
integer, intent(in) :: bps(:)
character(LEN(str)+SIZE(bps)) :: split
integer i
split=''
split(1:bps(1)-1) = str(1:bps(1)-1)
do i=1, SIZE(bps)-1
split(bps(i)+i:bps(i+1)-1+i) = str(bps(i):bps(i+1))
end do
split(bps(i)+SIZE(bps):) = str(bps(i):)
end function
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.
Here's test code:
program testcase
implicit none
integer :: ios, lu
type derived
integer :: a
end type derived
type (derived) :: d
namelist /test/ d
lu = 3
open (lu, file = 'test.dat', status='old', iostat=ios)
read (lu, nml = test, iostat=ios)
if (ios /= 0) then
write (*, *) 'error!'
else
write (*, *) 'good!', d % a
endif
end program testcase
This program reads an input file test.dat which contains a namelist for test whose type is a derived type derived.
When I try next content for test.dat it works fine(it prints good! 7):
&test
d%a = 7
/
However, with next content, I get an error:
&test
d % a = 7
/
Equal sign must follow namelist object name d
What's different is the whitespaces around % sign for component access in derived type.
I've tested with GNU Fortran(gfortran) 5.3.0. I also heard from my colleague that same problem occurred with latest Intel Fortran compiler. He also insisted that the old version of Intel Fortran compiler worked fine with both cases.
Is this behavior is normal? That is, does the standard forbid whitespaces around % in namelist input file, while whitespaces around % are allowed in source code?
Or, is this a bug of compiler or implementation of standard library?
Finally, I found some references which mention this problem.
From http://technion.ac.il/doc/intel/compiler_f/main_for/lref_for/source_files/pghnminp.htm ,
&group-name object = value [, object = value] .../
...
object
Is the name (or subobject designator) of an entity defined in the
NAMELIST declaration of the group name. The object name must not
contain embedded blanks except within the parentheses of a subscript
or substring specifier. Each object must be contained in a single
record.
Another one from http://docs.cray.com/books/S-3693-51/html-S-3693-51/i5lylchri.html ,
2.13.1.1. Names in Name-value Pairs
...
A name in an input record must not contain embedded blanks. A name in the name-value pair can be preceded or followed by one or more
blanks.
So, apparently, it seems that whitespaces in name are never allowed.
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.