1

this code should read a positive number and if user enter a non-numeric value, it asking him again to enter a number and wait the input to check again till entering a number

 do
   {
        printf("Enter a positive number: ");
   }
  while ( (scanf("%d%c", &n,&c) != 2 || c!='\n' || n<0) ) ;

but actually when entering non-numeric it keeps doing the body of the while loop without reading the waiting to check the condition of the while loop while(scanf("%d%c", &n,&c) != 2 || c!='\n' || n<0)

when edit the condition to

int clean_stdin()
{
    while (getchar()!='\n');
    return 1;
}

 do
   {
        printf("Enter a positive number: ");
   }
  while ( (scanf("%d%c", &n,&c) != 2 || c!='\n' || n<0) && clean_stdin() ) ;

It executes in the right way, but I don't understand why we need to add getchar() although we already use scanf() in the condition

4 Answers4

3

When scanf("%d%c", ... encounters non-numeric input, the "%d" causes the scanning to stop and the offending character to remain in stdin for the next input function. The "%c" does not get a chance to read that non-numeric character.

If code re-reads stdin with the same scanf("%d%c", ..., the same result. Some other way is needed to remove the non-numeric input. getchar(), getch(), etc. will read any 1 character.

Example code GetPositiveNumber()

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
2

Consider using fgets to collect input. Any invalid input is already removed from the input stream making it simpler to try again.
Parse the input with sscanf or others as needed.

char input[40] = "";
int valid = 0;
int n = 0;

do {
    printf ( "Enter a positive integer\n");
    if ( fgets ( input, sizeof ( input), stdin)) {
        int last = 0;
        if ( 1 == ( valid = sscanf ( input, "%d%n", &n, &last))) {
            if ( n < 0  || input[last] != '\n') {
                valid = 0;//reset if negative or last is not newline
            }
        }
    }
    else {
        fprintf ( stderr, "problem getting input from fgets\n");
        return 0;
    }
} while ( valid != 1);
xing
  • 2,125
  • 2
  • 14
  • 10
0

Because scanf() with most specifiers ignores white spaces, and you need to collect them before calling scanf() again if a '\n' is left in the buffer. If you don't collect them (and discard them immediately), then scanf() will return immediately on the next call.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
0

See this code

#include <stdio.h>

int
main(int argc, char* argv[]) {
  char c = 0;
  scanf("%c", &c);
  printf("%c\n", c);
  scanf("%c", &c);
  printf("%c\n", c);
  return 0;
}

If you type over two or three characters, second call of scanf doesn't work as interructive. And getch() is not standard. Also you shouldn't call getchar() since it may block. You can use fseek.

Hack using fseek works only on Windows. :-(

BAD SOLUTION

#include <stdio.h>

int
main(int argc, char* argv[]) {
  char c = 0;
  scanf("%c", &c);
  printf("%c\n", c);
  fseek(stdin, 0, SEEK_END);
  scanf("%c", &c);
  printf("%c\n", c);
  return 0;
}

GOOD SOLUTION

#include <stdio.h>

int
main(int argc, char* argv[]) {
  char buf[BUFSIZ];
  char c = 0;
  scanf("%c", &c);
  printf("%c\n", c);
  while (!feof(stdin) && getchar() != '\n');
  scanf("%c", &c);
  printf("%c\n", c);
  return 0;
}

This works good on Windows & Linux

mattn
  • 7,571
  • 30
  • 54
  • Using `fseek()` on a terminal on a Unix-like system does nothing; terminals are not seekable devices. – Jonathan Leffler Jul 03 '17 at 17:43
  • Hmm, you are right. I works only on Windows. How about use `setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdin, NULL, _IONBF, BUFSIZ);` ? – mattn Jul 03 '17 at 17:49
  • For my money, use `getchar()` until newline (or EOF). If that's not OK, then pretty much any solution is platform-specific. Platform-specific isn't a problem as long as the platform(s) where it works is (are) clearly identified. Don't present a platform-specific solution as a universal solution. Sometimes, though, you're aren't aware in advance that a solution _is_ platform-specific. For example, Microsoft supports `fflush(stdin)`; Unix-like systems do not. Microsoft may support the `fseek()` on terminal input streams; Unix-like systems do not. If you aren't aware of this, it's hard. – Jonathan Leffler Jul 03 '17 at 18:01
  • updated post. and it seems that checking feof at first is better for me. – mattn Jul 03 '17 at 18:04
  • Instead of checking `feof()` after using `c`, recommend checking the return value of `scanf()` - it it 1? – chux - Reinstate Monica Jul 03 '17 at 18:13
  • even though scanf return 1, buffer will be filled with rest characters. – mattn Jul 03 '17 at 18:17
  • Hmm, Window support rewind(stdin), but Windows doesn't support select(fileno(stdin)). Maybe platform-compatible solution not exists. – mattn Jul 03 '17 at 18:24
  • 2
    `scanf("%c", &c);` can return `EOF`. Then `printf("%c\n", c);` is undefined behavior. Better to check the return value of `scanf()` first before using `c`. – chux - Reinstate Monica Jul 03 '17 at 19:33
  • Note that [`while (!feof(file))` is always wrong](http://stackoverflow.com/questions/5431941/while-feof-file-is-always-wrong), but you're using a slightly different construct which is 'OK'. I'd still prefer: `int c; while ((c = getchar()) != EOF && c != '\n') ;` if only because it is one function call, not two. – Jonathan Leffler Jul 03 '17 at 20:47