0
  int valid = 0;
  while (!valid) {

    printf("\nEnter number of characters (1-30)> ");
    scanf("%d", & n);
    printf("\nInclude numbers (Y/N)> ");
    scanf(" %c", & nf);
    printf("\nInclude symbols (Y/N)> ");
    scanf(" %c", & sf);
    //cond1 and cond2 initialized here
    if (cond1 && cond2)
      valid = 1;
    else
      printf("Invalid input");
  }

I need to implement a faulty input detection functionality for the first integer scan. If the user enters a character instead of an integer the second scanf is skipped and it goes directly to the 3rd scanf. How can I stop this from happening in case of a character input on %d? I want to ask for the input again if the user enters a character instead of a number

zerb
  • 3
  • 3
  • 1
    You haven't clarified if you want your program to terminate or ask for input again. – alex01011 May 10 '21 at 12:52
  • 1
    Please re-work your question to include a [mcve]. Consider using more specific words in your instructions to user, such as _"enter from 1-30 digits"_ if that is what you want. Or _"enter from 1 to 30 alpha characters"_ if that is what you want. _characters_ is ambiguous. The entire table of ASCII characters are _characters_. – ryyker May 10 '21 at 12:59
  • @alex01011 I want to ask for the input again if the user enters a character instead of a number – zerb May 10 '21 at 13:02
  • Your last comment should be part of your post, not a comment. Edit your post please. – ryyker May 10 '21 at 13:04
  • Where are `cond1` and `cond2` created/initialized/set in your code? – ryyker May 10 '21 at 13:07
  • the `scanf()` function returns a value. That value is the number of successful 'input conversion specifiers' (or EOF) You should use the returned value to determine if a `%d` is successful or not – user3629249 May 11 '21 at 14:55

3 Answers3

2

Just check the return value of scanf(), in your case:

if ((scanf("%d", &n) != 1) /* should return 1 if 1 int is read */
{ 
  fprintf(stderr,"Input not a number\n");
  exit(EXIT_FAILURE) /* include stdlib or just use return */
}

Note that scanf() doesn't provide any protection from arithmetic overflow, in the case of a big number. You might want to use fgets() and later parse that string with a function such as strtol() for safety.

Example: https://godbolt.org/z/oaMhac983

If you want to ask for input again, I would suggest that you use fgets() instead and later check that string for non-digit characters.

You can use strspn(),it returns the number of characters from the first argument that are present in the second argument.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_N 13 /* save space \0 and \n */

int main(void) {
  int num = 0;
  char n[MAX_N] = {0};
  char *numbers = "-+0123456789";

  while (1) {
    if (fgets(n, MAX_N, stdin) == NULL) /* check return value of fgets() */
    {
      exit(EXIT_FAILURE);
    }
    if (strspn(n, numbers) ==
        strlen(n) - 1) /* only numbers were read (exclude '\n')*/
    {
      if (sscanf(n, "%d", &num) != 1) /* check return value of scanf() */
      {
        exit(EXIT_FAILURE);
      }
      break;
    }
  }

  printf("%d\n", num);

  return 0;
}

Example: https://godbolt.org/z/d5KTrTGGE

alex01011
  • 1,670
  • 2
  • 5
  • 17
  • In my code if I try: if ((scanf("%d", &n) != 1)) continue; it ends up being an infinite loop instead of asking for the input again in the next iteration. – zerb May 10 '21 at 12:58
  • @zerb I updated my answer. You can go with that, or with Vlad's, you can modify mine to make use of the `isdigit()` is function if you want it simpler. – alex01011 May 10 '21 at 13:21
  • `char *numbers = "0123456789";` is insufficient for inputs like `"-123\n", "+456\n", "0x789\n", "123"`. – chux - Reinstate Monica May 10 '21 at 14:50
  • @chux-ReinstateMonica I've included the prefix option, as for the hex option, it would require a modification in the `scanf()` format specifier. – alex01011 May 10 '21 at 15:08
1

It seems you mean something like the following

#include <stdio.h>

int main(void) 
{
    int n;
    const int MIN = 1, MAX = 30;
    
    do
    {
        printf( "\nEnter number of characters (%d-%d)> ", MIN, MAX );
        
        if ( scanf( "%d", &n ) != 1 )
        {
            clearerr( stdin );
            scanf( "%*[^\n]" );
            scanf( "%*c" );
            n = 0;
        }
    } while ( n < MIN || MAX < n );
    
    printf( "n = %d\n", n );
    
    return 0;
}

The program output might look like

Enter number of characters (1-30)> Hello
Enter number of characters (1-30)> 0
Enter number of characters (1-30)> 31
Enter number of characters (1-30)> Bye
Enter number of characters (1-30)> 10
n = 10

Or something like

#include <stdio.h>

int main(void) 
{
    int n = 0;
    const int MIN = 1, MAX = 30;
    
    printf( "\nEnter number of characters (%d-%d)> ", MIN, MAX );
        
    if ( scanf( "%d", &n ) != 1 )
    {
        clearerr( stdin );
        scanf( "%*[^\n]" );
        scanf( "%*c" );
        n = 0;
    }

    char nf, sf;
    
    if ( n != 0 )
    {
        printf("\nInclude numbers (Y/N)> ");
        scanf( " %c", &nf );
    }
    else
    {
        printf("\nInclude symbols (Y/N)> ");
        scanf(" %c", &sf);
    }
    
    return 0;
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

How can I stop this from happening in case of a character input on %d?

Check the return value of scanf().

scanf("%d", &n); returns 1 of 3 values under various conditions:

1 Scanning succeed. Scanned int value in n.
0 Scanning failed due to non numeric input (the case OP is interested in). Value in n is unchanged.
EOF Scanning failed due to no more input (End-of-file). Value in n is unchanged.
EOF Scanning failed due to input error (rare). Value in n is indeterminate.

do { 
  printf("\nEnter number of characters (1-30)> ");
  int retval = scanf("%d", &n);
  if (retval == EOF) {
    printf("End of file or input error.  Quitting\n");
    exit(-1);
  }
  if (retval == 0) {
    // read in rest of line (except \n) and toss it.
    scanf("%*[^\n]");
    continue; 
  }
  // If here, valid `int` input read, now testfor range.
} while (n < 1 || n > 30);

....

Consider coding a helper function to write a prompt and read an int.

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