I am reading stdin in a loop and do some comparaison with a bunch of "if" to decide what to do according to the input.
Here is a shortened snippet of the code :
CHARACTER (len= :), allocatable :: input
CHARACTER (len=4096) :: inbuffer ! frustrating but... well, fortran :3
DO
! get input
READ(*, '(a)') inbuffer ! because apparently you can't have allocation on read so you can't just read "input". meh.
input = TRIM(inbuffer)
CALL debug_log(input)
IF(input .EQ. 'uci') THEN
CALL debug_log(" printing uci info")
!isready
ELSE IF(input .EQ. 'isready') THEN
CALL debug_log(" isready -> readyok")
WRITE(*, '(a)') "readyok"
!ucinewgame
ELSE IF(input .EQ. 'ucinewgame') THEN
CALL debug_log("not implemented : reset board and start a new game")
!position
ELSE IF(input(1:8) .EQ. 'position') THEN
CALL debug_log("not implemented : set position")
!quit -> exit main loop
ELSE IF(input .EQ. 'quit') THEN
CALL debug_log(" quit command issued, exiting main loop")
EXIT
!non uci command
!nothing yet
!unknown command
ELSE
CALL debug_log(" ignoring invalid command")
END IF
end do
The input will expect command like "position 123 23415 etc..."
If I type "posi" it's say it's an invalid command as expected.
If I type "position" it say it's not implemented as expected too.
However:
If I type "position": I get not implemented
Followed by "posi": it says "not implemented" instead of "invalid command"
My guess is that it read 8 character even if the input is only 4 and since the previous command was "position" it make posi + tion = position
Here is some log to demonstrate:
** opening debug file : 20181111 / 223418.127
223418.127 : Initializing Fortiche engine
223418.129 : Entering main loop
223420.859 : posi
223420.859 : ignoring invalid command
223426.467 : xxxxtion
223426.467 : ignoring invalid command
223430.498 : posi
223430.498 : not implemented : set position
223437.323 : xxxxxxxxx
223437.323 : ignoring invalid command
223439.418 : posi
223439.418 : ignoring invalid command
223443.979 : position
223443.979 : not implemented : set position
223447.122 : quit
223447.122 : quit command issued, exiting main loop
223447.122 : closing, bye
xxxxtion + posi = position
Which is clearly wrong but I can understand how it ended up like this.
Should I use something other than .EQ.?
When I print the input it clearly doesn't print the input + whatever garbage was left behind in memory. But it's doing it when comparing string of possibly different length.
What can I do to solve this problem?
I'm not even started with the hardcore parsing and I already have a problem.
I'm using GNU Fortran on Windows.
Yes, it's UCI stuff as Universal Chess Interface.
EDIT : Full source code : https://github.com/ker2x/fortiche (comment the dirty hack at line 107 & 108 to reproduce the problem)
Substring references need to have starting and ending positions that are within the limits of the string.
You don't defend against a string that has a length less than eight prior to the substring reference input(1:8) .eq. 'position'.
With input shorter than eight characters, your program is non-conforming, anything can then happen, where anything very reasonably includes the behaviour you see.
Runtime debugging options may help to catch this programming error, depending on the capabilities of your compiler.
Related
I'm writing in fortran (90). My program must read file1, do something with every line of it and write result to file2. But the problem - file1 has some unneeded information in first line.
How can I skip a line from input file using Fortran?
The code:
open (18, file='m3dv.dat')
open (19, file='m3dv2.dat')
do
read(18,*) x
tmp = sqrt(x**2 + 1)
write(19, *) tmp
end do
First line is a combination of text and numbers.
One possible solution has already been presented to you which uses a "dummy variable", but I just wanted to add that you don't even need a dummy variable, just a blank read statement before entering the loop is enough:
open(18, file='m3dv.dat')
read(18,*)
do
...
The other answers are correct but this can improve conciseness and (thus) readability of your code.
Perform a read operation before the do loop that reads whatever is on the first line into a "dummy" variable.
program linereadtest
implicit none
character (LEN=75) ::firstline
integer :: temp,n
!
!
!
open(18,file='linereadtest.txt')
read(18,*) firstline
do n=1,4
read(18,'(i3)') temp
write(*,*) temp
end do
stop
end program linereadtest
Datafile:
This is a test of 1000 things that 10
of which do not exist
50
100
34
566
!ignore the space in between the line and the numbers, I can't get it to format
open (18, file='m3dv.dat')
open (19, file='m3dv2.dat')
read(18,*) x // <---
do
read(18,*) x
tmp = sqrt(x**2 + 1)
write(19, *) tmp
end do
The line added just reads the first line and then overwrites it with the seconde on the first iteration.
This question already has answers here:
Extract substring of Fortran string array
(3 answers)
Extract a single character from a Fortran string
(1 answer)
Access character at specific index in a string in Fortran
(1 answer)
Closed 3 years ago.
I would like to evaluate if the 3rd letter of variable myline is 'C' or not.
I try this:
program main
implicit none
type line
integer :: count = 5
character(len=48) :: list = 'ABCDE'
end type
type(line) :: myline
character(len=1) :: letter = 'C'
write(*,*) myline%count, myline%list
if(myline%list(3) == letter) then
write(*,*) 'TRUE'
else
write(*,*) 'FALSE'
end if
end program
But I get:
$ /usr/local/bin/gfortran8 -mcmodel=medium -fcheck=all -Wl,-rpath=/usr/local/lib/gcc8 -o test test.f90
test.f90:15:15:
if(myline%list(3) == letter) then
1
Error: Syntax error in IF-expression at (1)
test.f90:17:5:
else
1
Error: Unexpected ELSE statement at (1)
test.f90:19:4:
end if
1
Error: Expecting END PROGRAM statement at (1)
I am using gfortran (gcc8) and the Fortran 90 standard.
In Fortran, a character substring reference always needs a start and end position. So what you want here is myline%list(3:3).
You can omit the end position (retaining the colon), for example (3:), and that means the rest of the string. Similarly you can omit the start position and it means from the first character (:3).
As a suggestion, letter would be better declared with the parameter attribute as it is a constant, but what you have would work.
I am trying to repeat the string "k" to match the length of "text" without going over the length. So it would output "treetreetreetreetreet" and not "treetreetreetreetreetree". I really dont know where to start other than just outputing more characters than needed.
PROGRAM test
IMPLICIT NONE
CHARACTER*30 :: text, k
INTEGER :: times
text = 'hello my name is anon'
k = 'tree'
times = (LEN_TRIM(text)/LEN_TRIM(k)) + 1
WRITE(*,*) REPEAT(k,times)
END PROGRAM test
First, your sample program as-is doesn't produce treetreetreetreetreetree as you expect, it actually produces tree tree tree .... When you pass the string k to REPEAT, the spaces after tree also get repeated. You should trim the string before repeating it, such as REPEAT(trim(k),times).
There are several ways to solve your main problem - I recommend using what you have so far but reducing the final result to the length you want - in this case LEN_TRIM(text). A good way to do this is to store the output of REPEAT in a temporary variable and output only a subset of this final string.
With both of these modifications and some other cleanup, your code looks like:
program main
implicit none
character(len=30) :: text, k, str
integer :: times
text = 'hello my name is anon'
k = 'tree'
times = (LEN_TRIM(text)/LEN_TRIM(k)) + 1 ! -- Note integer division
str = REPEAT(trim(k),times)
write(*,*) str(1:LEN_TRIM(text))
end program main
which gives the desired output
> gfortran main.f90 && ./a.out
treetreetreetreetreet
for(i=getchar();; i=getchar())
if(i=='x')
break;
else putchar(i);
Answer is : mi
Can someone explain this piece of code ?(MCQ Question)
This question can be solved by eliminating incorrect answer. This fragments prints character and exits loop if the character is an x. So the program would not output an x.
Any output string that doesn't contain x is possible. In your MCQ, possibly mi is the only option with x and all other options contain x somewhere in the string making them incorrect answer.
If input is "mix....", output would be "mi". Below is your loop unrolled.
getchar() -> m -> else -> print m /* First getchar */
getchar() -> i -> else -> print i /* Second getchar */
getchar() -> x -> if -> break /* Second getchar */
for(i=getchar();; i=getchar())
if(i=='x')
break;
else putchar(i);
your Code will keep on running till it encounter 'x' so whatever input you give, it will read character by character as you have used getchar() function..
If character is 'x' then break the loop.
else print character.
like, If the input is
sparx
output will be
spar
The for loop
for(i=getchar();; i=getchar())
and syntax and structure of the for loop is
for ( variable initialization; condition; variable update )
as i = getchar() will read char 'i' it is ok. next there is no condition and final in updating you are again reading a character so it a infinite loop.
Loop will terminate only when it will encounter 'x' as the statement
if(i=='x')
break;
Otherwise it will keep on printing the character.
else putchar(i);
Here is the Demo.
Hope it helps!!
I am trying to put an input string into sub-string arrays. The number of data in the input file are less than 10 but unknown. The number of spaces between each data is also unclear.
Example:
Asd B Cwqe21 Ddsw Eww
I am quite novice to Fortran, so I do not know which format I should use. My problem is that I do not know the number of data (here I assumed that there are 5), so how can I make the code work?
I tried the following which did not work:
CHARACTER (LEN=100), DIMENSION(10) :: string
READ (1,*) (string,I=1,10)
It seems that the error I got was because there was no 6th string to read and put into string(6).
I tried using the "Index" to find the space, but since I do not know how many spaces are in the string, it did not help me.
I don't know if this is more or less elegant/efficient than the standard approach in M.S.B's comment, but an interesting alternative.
integer istart,nw
character (len=100) line,wd,words(100)
open(1,file='t.dat')
read(1,'(a)')line
istart=1
nw=0
do while(len(trim(line(istart:))).gt.0)
read(line(istart:),*)wd
istart=istart+index(line(istart:),trim(wd))+len(trim(wd))
nw=nw+1
words(nw)=trim(wd)
enddo
write(*,*)trim(line)
write(*,*)('/',trim(words(k)),k=1,nw),'/'
end
An inefficient approach that is simple to program is to try to read the maximum number of items, and if this fails to successively try to read one fewer items until the read is successful, as shown below:
program xread_strings
integer, parameter :: nw = 10
character (len=1000) :: text
character (len=20) :: words(nw)
integer :: i,ierr,nread
text = "Asd B Cwqe21 Ddsw Eww"
nread = 0
do i=nw,1,-1
read (text,*,iostat=ierr) words(:i)
if (ierr == 0) then
nread = i
exit
end if
end do
if (nread > 0) write (*,*) "read ",nread," words: ",("'"//trim(words(i)) // "' ",i=1,nread)
end program xread_strings
! g95 Output:
! read 5 words: 'Asd' 'B' 'Cwqe21' 'Ddsw' 'Eww'