1

I have the following code.

           int x1, x2;
           char check[3];
           do{
                //input should be (x1,x2)
                successfully_inputed = scanf("(%d,%d%2s", &x1, &x2, check);
                if(successfully_inputed == EOF)
                        break;
                if(check[1] || successfully_inputed != 3 || check[0] != ')'){
                        fprintf(stderr, "Invalid line");
                        
                        char ch;
                        while((ch = getchar()) != EOF && ch != '\n');

                        continue;
                }
                getchar(); // remove the new line character from buffer
          }while(1);

It does work for every input i give other than a particular one. When i give anything and then followed by the EOF, for example "asdEOF" then it consumes "asd" and when it reaches EOF it asks me for input, not the scanf() but the getchar(). Why is this happening and how can i solve it?

yad0
  • 77
  • 7
  • 3
    `getchar` returns an `int`. – dbush Jan 12 '23 at 22:29
  • changing ```ch``` to ```int``` didn't solve it @dbush – yad0 Jan 12 '23 at 22:33
  • This looks like some kind of psuedo-code. Can you give us real code we can compile and run ourselves? – pmacfarlane Jan 12 '23 at 22:39
  • edited it. if you want more code, i can provide more, i just wanted to keep it clean and simple. @pmacfarlane – yad0 Jan 12 '23 at 22:46
  • Similar question is [here](https://stackoverflow.com/questions/60843089/c-scanf-function-how-to-terminate-on-eof-eot). You should probably check `if (feof(stdin)) break;` after your `scanf()`. (This fixed your problem for me, once I got your code to compile.) – pmacfarlane Jan 12 '23 at 23:03
  • This probably isn't the advice you're looking for, but: flushing the input buffer is very nearly impossible, and it isn't worth it, because it's only necessary in the first place if you're trying to use `scanf` to do input. If you're still using `scanf` to do input, just don't worry about flushing the input, just don't worry about handling bad input for now. If you want to handle bad input, you can't do it with `scanf`: use [something better](https://stackoverflow.com/questions/58403537). See also [this question](https://stackoverflow.com/questions/7898215). – Steve Summit Jan 12 '23 at 23:05
  • When you say "asdEOF" do you mean that literal string. Usually, you trigger EOF with ctrl-d (or an input stream ending). – Allan Wind Jan 12 '23 at 23:07
  • yes, that's how i do @AllanWind – yad0 Jan 12 '23 at 23:08
  • Sorry, not clear what you meant. You shouldn't look at check[1] before you know scanf() was successful (i.e. returned 3). If I give your program the literal input "asdEOF" it gives me "Invalid line" as it didn't match anything, then goes back to scanf(). – Allan Wind Jan 12 '23 at 23:10
  • if the input was correct, then check[1] should have ```'\0'```, that's why i check it. the input has to be (x1,x2). why should i check it? – yad0 Jan 12 '23 at 23:14
  • check[1] is uninitialized till scanf() returns 2 or 3. If your input is "asd" then your program is hanging on the flush getchar() waiting for input instead of getting the EOF at least in gdb. – Allan Wind Jan 12 '23 at 23:15
  • it does give "Invalid line" but then you type 2 other lines before it gives "Invalid line" again. i also checked it with the gdb and saw that ```getchar()``` asks for input @AllanWind – yad0 Jan 12 '23 at 23:16
  • It works if you do `echo -n asd | ./a.out` but hangs when it's an interactive tty. – Allan Wind Jan 12 '23 at 23:22
  • The short of it is that you need to type ctrl-d twice as the first one will not close the stream. "On linux Ctrl-D only works when the buffer is already empty otherwise it just flushes it. Therefore unless he has pressed enter without any characters after that, he will have to press Ctrl-D twice." – Allan Wind Jan 12 '23 at 23:31
  • Does this answer your question? [End of File (EOF) in C](https://stackoverflow.com/questions/4358728/end-of-file-eof-in-c) – Allan Wind Jan 12 '23 at 23:34

1 Answers1

1

When the input is "asd<ctr-d>" scanf() fails so you need to swap the order of conditions to you check it first:

        if(successfully_inputed != 3 || check[1] || check[0] != ')'){

In either case we are correctly getting an "Invalid line". Then we try to flush the input stream (ensuring ch is int):

int ch;
while((ch = getchar()) != EOF && ch != '\n');

This result in getchar() reading the 'a' and 's' and 'd' then hanging waiting for input. This is because, on Linux, the ctrl-d only cause the terminal buffer to be flushed, and you need a 2nd ctrl-d on an empty stream to close it. This will then trigger EOF being returned by getchar().

Even if you switch to reading input with fgets() it will not return till you press ctrl-d twice on non-empty input buffer:

#include <stdio.h>
#define LINE_LEN 100
#define CHECK_LEN 3

int main() {
    for(;;) {
        char line[LINE_LEN];
        if(!fgets(line, LINE_LEN, stdin)) {
            if(feof(stdin)) break;
            // handle error
            return 1;
        }
        int x1;
        int x2;
        char check[CHECK_LEN];
        int rv = sscanf(line, "(%d,%d%2s", &x1, &x2, check);
        if(rv != 3 || check[1] || check[0] != ')') {
            fprintf(stderr, "\nInvalid line\n");
        }
    }
}

and example session:

$ ./a.out
asd<ctrl-d><ctrl-d>
Invalid line
$
$ ./a.out
<ctrl-d>
$
Allan Wind
  • 23,068
  • 5
  • 28
  • 38