scanf validation sits and waits for another input. Why? - c

I was working on this sample exercise, and everything works as I would like it to, but there is one behavior I don't understand.
When providing input: if I make consecutive invalid entries everything seems to work great. But if I enter a number different from 1,2,3 in the case of the first question, or 1,2 in the case of the second question, the program just sits there until a new input is given. If another invalid entry is made, it goes back to the error "invalid entry" message, and if an appropriate number is entered, everything moves along fine.
I do not understand why it stops to wait for a second input...anyone?
Thanks guys.
#include <stdio.h>
static int getInt(const char *prompt)
{
int value;
printf("%s",prompt);
while (scanf("%d", &value) !=1)
{
printf("Your entry is invalid.\nGive it another try: %s", prompt);
getchar();
scanf("%d", &value);
}
return value;
}
int main() {
int wood_type, table_size, table_price;
printf("Please enter " );
wood_type = getInt("1 for Pine, 2 for Oak, and 3 for Mahogany: ");
printf("Please enter ");
table_size = getInt("1 for large, 2 for small: ");
printf("\n");
switch (wood_type) {
case 1:
table_price = (table_size == 1)? 135:100;
printf("The cost of for your new table is: $%i", table_price);
break;
case 2:
table_price = (table_size == 1)? 260:225;
printf("The cost of for your new table is: $%i", table_price);
break;
case 3:
table_price = (table_size == 1)? 345:310;
printf("The cost of for your new table is: $%i", table_price);
break;
default:
table_price = 0;
printf("The cost of for your new table is: $%i", table_price);
break;
}
}

You most likely need to flush your input buffer (especially with multiple scanf calls in a function). After scanf, a newline '\n' remains in the input buffer. fflush does NOT do this, so you need to do it manually. A simple do...while loop works. Give it a try:
edit:
static int getInt(const char *prompt)
{
int value;
int c;
while (printf (prompt) && scanf("%d", &value) != 1)
{
do { c = getchar(); } while ( c != '\n' && c != EOF ); // flush input
printf ("Invalid Entry, Try Again...");
}
return value;
}
The blank line you get if you enter nothing is the normal behavior of scanf. It is waiting for input (some input). If you want your routine to immediately prompt again in the case the [Enter] key is pressed, then you need to use another routine to read stdin like (getline or fgets). getline is preferred as it returns the number of characters read (which you can test). You can then use atoi (in <stdlib.h>) to convert the string value to an integer. This will give you the flexibility you need.
example:
int newgetInt (char *prompt)
{
char *line = NULL; /* pointer to use with getline () */
ssize_t read = 0; /* number of characters read */
size_t n = 0; /* numer of chars to read, 0 no limit */
static int num = 0; /* number result */
while (printf ("\n %s ", prompt) && (read = getline (&line, &n, stdin)) != -1)
{
if ((num = atoi (line)))
break;
else
printf ("Invalid Input, Try Again...\n");
}
return num;
}

If some invalid input is entered, it stays in the input buffer.
The invalid input must be extracted before the scanf function is completed.
A better method is to get the whole line of input then work on that line.
First, put that input line into a temporary array using fgets(),
then use sscanf() (safer than scanf because it guards against overflow).
#include <stdio.h>
int main(int argc, const char * argv[]) {
char tempbuff[50];
int result, d , value;
do
{
printf("Give me a number: ");
fgets( tempbuff, sizeof(tempbuff), stdin ); //gets string, puts it into tempbuff via stdin
result = sscanf(tempbuff, "%d", &value); //result of taking buffer scanning it into value
if (result < 1){ //scanf can return 0, # of matched conversions,
//(1 in this case), or EOF.
printf("You didn't type a number!\n");
}
}while (result < 1);
//some code
return 0;
}
Knowledge from: http://www.giannistsakiris.com/2008/02/07/scanf-and-why-you-should-avoid-using-it/

Related

Does scanf need to be checked for number of conversions if using a default switch statement?

I have a scanf function that takes an integer 1,2, or 0. My default statement will return to a loop if there is not an input of these integers. If a character is entered, the default case still works, displays an error and returns to main. My question is if I should be using another while loop to check the scanf function for an integer, or if it is okay to keep the default statement which will return for any invalid input.
int main(int argc, char* argv[]) {
int value;
value = length_orWeight();
while (value != 0) {
value = length_orWeight();
}
return 0;
}
int length_orWeight(void) {
int choice;
printf("\nWhat would you like to convert?\n0.End Program 1.Lengths 2.Weights: ");
scanf("%d", &choice);
clear_keyboard_buffer();
switch (choice) {
case 1:
convert_lengths();
return 1;
case 2:
convert_weights();
return 2;
case 0:
return 0;
default:
printf("\nError: You must enter 0, 1, or 2.\n");
return 3;
}
}
Not checking the return value of scanf is inadequate.
The C standard does not specify what happens to objects that are not assigned values because input terminated early (as when a non-matching character was found, an end-of-file was encountered, or an input error occurred). A common behavior may be for scanf to leave the object (choice) unchanged. If this occurs in your first execution of scanf, it will remain uninitialized. However, as the standard does not define what scanf does in this regard, other behaviors are possible. For example, a rudimentary scanf implementation could, upon starting to work on %d, initialize choice to zero, in anticipation of building a number in it digit-by-digit. When it then reads a character and finds some non-digit the user entered, it could terminate with a return value indicating no conversion was performed, with choice still set to zero.
If scanf does not return a value that indicates the a conversion was completed that assigned a value to an object you passed it, you do not know what value is in that object, even if you initialized the object prior to calling scanf. Therefore, before using the object at all, you must check the return value of scanf.
Eric Postpischil clearly stated that checking scanf's return value is necessary, but didn't point to the fact that it is a very bad function for reading input from the user. Better use fgets and sscanf:
// Reads one line
char *scanline(char *line, size_t size, FILE *istream)
{
if (!fgets(line, size, istream)) // Input failed
return NULL;
// Replace newline with null-terminator
size_t nl = strcspn(line, "\n");
line[nl] = '\0';
return line;
}
// Reads an int
bool scanint(int *i, FILE* istream)
{
char buffer[255];
if (!scanline(buffer, sizeof buffer, istream))
return false;
if (sscanf(buffer, "%d", i) != 1)
return false;
return true;
}
Also, make sure that clear_keyboard_buffer() is not fflush(stdin):
void clear_keyboard_buffer(void)
{
scanf("%*[^\n]"); // Read and discard everything until hitting a newline.
}
Here is how your code would look like:
int length_orWeight(void)
{
int choice;
printf("\nWhat would you like to convert?\n0.End Program 1.Lengths 2.Weights: ");
while (!scanint(&choice, stdin)) {
printf("Please enter a valid number: ");
};
// clear_keyboard_buffer(); // No longer necessary
switch (choice) {
case 1:
//convert_lengths();
return 1;
case 2:
//convert_weights();
return 2;
case 0:
return 0;
default:
printf("\nError: You must enter 0, 1, or 2.\n");
return 3;
}
}
int main(int argc, char* argv[])
{
int value;
value = length_orWeight();
while (value != 0) {
value = length_orWeight();
}
}

C Program - How to deny any non-numerical input

I've just started learning the language of C, and would love your help in cleaning up / simplifying my code if you know a better way to reach the following.
I want a program to ask for a number, and if that is found then proceed to print and end, however if anything else is put in (e.g. a letter key), then I want the program to loop asking for a number until one is given.
I started off by using a simple scanf input command, but this seemed to go into an infinite loop when I tried to check if a valid number (as we define them) was put in.
So instead I have ended up with this, from playing around / looking online, but I would love to know if there is any more efficient way!
//
// Name & Age Program
// Created by Ben Warren on 1/3/18.
//
#include <stdio.h>
int main (void)
{
//Setting up variables
int num;
char line[10]; /* this is for input */
//Collecting input
printf("Please enter any number? \t");
scanf("%d", &num);
//If Invalid input
while (num==0)
{
printf("\nTry again:\t");
fgets(line, 10, stdin); //turning input into line array
sscanf(line, "%d",&num); //scaning for number inside line and storing it as 'num'
if (num==0) printf("\nThat's not an number!");
}
//If Valid input
{
printf("\n%d is nice number, thank you! \n\n", num);
*}*
return 0;
}
Instead of checking if the value is different to 0, check the return value of
sscanf. It returns the number of conversions it made. In your case it should be 1. Unless the return value is 1, keep asking for a number.
#include <stdio.h>
int main(void)
{
int ret, num;
char line[1024];
do {
printf("Enter a number: ");
fflush(stdout);
if(fgets(line, sizeof line, stdin) == NULL)
{
fprintf(stderr, "Cannot read from stdin anymore\n");
return 1;
}
ret = sscanf(line, "%d", &num);
if(ret != 1)
fprintf(stderr, "That was not a number! Try again.\n");
} while(ret != 1);
printf("The number you entered is: %d\n", num);
return 0;
}
That is not a bad approach for someone new to C. One small improvement would be to actually check the return value of scanf(), since it returns the number of arguments successfully retrieved. Then you could get away from relying on num being 0 to indicate the input was valid. Unless you do want to specifically flag 0 as invalid input.
int ret = scanf("%d", &num);
ret == 1 would mean an integer was succesffully read into num, ret == 0 would mean it was not.
Consider using strtol to parse a string for a long int. This also allows you to detect trailing characters. In this example if the trailing character is not a newline, the input can be rejected. strtol can also detect overflow values. Read the documentation to see how that works.
#include <stdio.h>
#include <stdlib.h>
int main (void)
{
//Setting up variables
long int num = 0;
char line[40] = ""; /* this is for input */
char *parsed = NULL;
printf("Please enter any number? \t");
fflush ( stdout);
while ( fgets(line, 40, stdin))
{
parsed = line;//set parsed to point to start of line
num = strtol ( line, &parsed, 10);
if ( parsed == line) {//if parsed equals start of line there was no integer
printf("Please enter a number? \t");
printf("\nTry again:\t");
fflush ( stdout);
continue;
}
if ( '\n' != *parsed) {//if the last character is not a newline reject the input
printf("Please enter only a number? \t");
printf("\nTry again:\t");
fflush ( stdout);
}
else {
break;
}
}
if ( !parsed || '\n' != *parsed) {
fprintf ( stderr, "problem fgets\n");
return 0;
}
printf("\n%ld is nice number, thank you! \n\n", num);
return 0;
}
0 (zero) is a number...
But I see what you want to do...
You can check for a valid number, using isdigit or a combination of similar functions
I think its also important to follow the advice of other answers to use the return value from scanf using code such as:
int ret = scanf("%d", &num);
and examining ret for success or failure of scanf.

How to break out of loop for blank input

How do I break out of this while loop if the user just presses enter without typing anything.
int main()
{
while(1){
int integer;
printf("enter integer:");
scanf("%d",&integer);
}
return 0;
}
You can't. scanf is a horrible function and should be avoided (in particular, you should never use it for user input).
The easiest way to get user input working is to make sure all input is through fgets (which reads a whole line). You can then analyze that line, convert it to a number (e.g. with strtol, strtod, sscanf, ...), or do whatever you want with it.
Here's an example:
#include <stdio.h>
#include <string.h>
int main(void) {
while (1) {
char buf[200];
int integer;
printf("enter integer: ");
fflush(stdout);
if (!fgets(buf, sizeof buf, stdin)) {
/* input error or end of file reached */
break;
}
buf[strcspn(buf, "\n")] = '\0'; /* remove trailing newline, if any */
if (buf[0] == '\0') {
/* empty line */
break;
}
sscanf(buf, "%d", &integer);
}
return 0;
}
According to me...
Take input as a string
IF inputString equals to lineBreak
THEN break
OTHERWISE convert it into integer
int main()
{
while(1)
{
int integer;
char input[1024];
printf("enter integer:");
fgets (input, sizeof (input), stdin);
if (strcmp (input, "\n") == 0)
break;
integer = atoi(input);
}
return 0;
}
It is possible,
Initialise the value of integer with something that is not allowed in input
For eg if the input takes positive integers ,I initialise integer with -1
int main()
{
while(1){
int integer=-1;
printf("enter integer:");
scanf("%d",&integer);
if(integer==-1)
break;
}
return 0;
}

How to check if the user input an integer using scanf

I created a program to make a diamond out of *'s. I am looking for a way to check if the type of input is an integer in the C language. If the input is not an integer I would like it to print a message.
This is what I have thus far:
if(scanf("%i", &n) != 1)
printf("must enter integer");
However it does not display the message if it's not an integer. Any help/guidance with this issue would be greatly appreciated!
you can scan your input in a string then check its characters one by one, this example displays result :
0 if it's not digit
1 if it is digit
you can play with it to make your desired output
char n[10];
int i=0;
scanf("%s", n);
while(n[i] != '\0')
{
printf("%d", isdigit(n[i]));
i++;
}
Example:
#include <stdio.h>
#include <string.h>
main()
{
char n[10];
int i=0, flag=1;
scanf("%s", n);
while(n[i] != '\0'){
flag = isdigit(n[i]);
if (!flag) break;
i++;
}
if(flag)
{
i=atoi(n);
printf("%d", i);
}
else
{
printf("it's not integer");
}
}
Use fgets() followed by strtol() or sscanf(..."%d"...).
Robust code needs to handle IO and parsing issues. IMO, these are best done separately.
char buf[50];
fgets(buf, sizeof buf, stdin);
int n;
int end = 0; // use to note end of scanning and catch trailing junk
if (sscanf(buf, "%d %n", &n, &end) != 1 || buf[end] != '\0') {
printf("must enter integer");
}
else {
good_input(n);
}
Note:
strtol() is a better approach, but a few more steps are needed. Example
Additional error checks include testing the result of fgets() and insuring the range of n is reasonable for the code.
Note:
Avoid mixing fgets() and scanf() in the same code.
{ I said scanf() here and not sscanf(). }
Recommend not to use scanf() at all.
strtol
The returned endPtr will point past the last character used in the conversion.
Though this does require using something like fgets to retrieve the input string.
Personal preference is that scanf is for machine generated input not human generated.
Try adding
fflush(stdout);
after the printf. Alternatively, have the printf output a string ending in \n.
Assuming this has been done, the code you've posted actually would display the message if and only if an integer was not entered. You don't need to replace this line with fgets or anything.
If it really seems to be not working as you expect, the problem must be elsewhere. For example, perhaps there are characters left in the buffer from input prior to this line. Please post a complete program that shows the problem, along with the input you gave.
Try:
#include <stdio.h>
#define MAX_LEN 64
int main(void)
{ bool act = true;
char input_string[MAX_LEN]; /* character array to store the string */
int i;
printf("Enter a string:\n");
fgets(input_string,sizeof(input_string),stdin); /* read the string */
/* print the string by printing each element of the array */
for(i=0; input_string[i] != 10; i++) // \0 = 10 = new line feed
{ //the number in each digits can be only 0-9.[ASCII 48-57]
if (input_string[i] >= 48 and input_string[i] <= 57)
continue;
else //must include newline feed
{ act = false; //0
break;
}
}
if (act == false)
printf("\nTHIS IS NOT INTEGER!");
else
printf("\nTHIS IS INTEGER");
return 0;
}
[===>] First we received input using fgets.Then it's will start pulling each digits out from input(starting from digits 0) to check whether it's number 0-9 or not[ASCII 48-57],if it successful looping and non is characters -- boolean variable 'act' still remain true.Thus returning it's integer.

C programming minor issue with interactive menu when input a floating value

My program works well with invalid inputs such as char, number out of range, but a problem happens when a floating point value such as 1.2 is entered. The program prints menu again, and asks user for input before printing error message. What I try to fix is don't print menu again, but still struggle. For example,
Make your selection: 1.1
[Here is menu content]
Make your selection: That selection isn't valid. Please try again.
#include <stdio.h>
#define QUIT 0
int menu();
int main(void)
{
int choice;
char c;
choice = menu();
while(choice != QUIT) //execute so long as choice is not equal to QUIT
{
choice = menu();
}
}
int menu(void)
{
int option;
printf("Text Encoder Service\n\n");
printf("1.\tEnter name of input file (currently 'Secret.txt')\n");
printf("2.\tEnter name of output file (currently not set)\n");
printf("3.\tEnter number of characters data should be shifted (currently +7)\n");
printf("4.\tEncode the text\n\n");
printf("0.\tQuit\n\n");
printf("Make your selection: ");
while( (scanf(" %d", &option) != 1) /* non-numeric input */
|| (option < 0) /* number too small */
|| (option > 4)) /* number too large */
{
fflush(stdin); /* clear bad data from buffer */
printf("That selection isn't valid. Please try again.\n\n");
printf("Your choice? ");
}
return option;
}
I finally could validate floating input. Thanks your advices so much! This is my new code. What else do you think an invalid input?
int menu(void)
{
int option, parsed_inputs;
char overcount_char;
parsed_inputs = 1;
printf("Text Encoder Service\n\n");
printf("1.\tEnter name of input file (currently 'Secret.txt')\n");
printf("2.\tEnter name of output file (currently not set)\n");
printf("3.\tEnter number of characters data should be shifted (currently +7)\n");
printf("4.\tEncode the text\n\n");
printf("0.\tQuit\n\n");
printf("Make your selection: ");
parsed_inputs = scanf_s("%d%c", &option, &overcount_char);
while( parsed_inputs > 1 ) /* number too large */
{
if((overcount_char != '\n') || (option < 0) || (option > 4))
{
fflush(stdin); /* clear bad data from buffer */
printf("That selection isn't valid. Please try again.\n\n");
printf("Your choice? ");
scanf_s("%d%c", &option, &overcount_char);
}
else
break;
}
return option;
}
An input of 1.1 leads to the following:
The string is read into an internal buffer.
It is then matched against the given format string.
On the first non-match, the scanf() call is stopped and it returns the number of successfully scanned values.
Let's test it:
#include <stdio.h>
int main(int argc, char ** argv)
{
while (1) {
int option;
int n = scanf(" %d", &option);
printf("%d %d\n", n, option);
if (n <= 0) break;
}
}
This program reads one line.
Suppose I enter 123 132.
Then the following happens:
* As the format string starts with a space, all leading whitespace is consumed. In this case, there is none.
* Then the 123 is consumed and put into option.
* As the format string is over now, parsing is stopped, n=1 and option=123.
* In the next loop run, the same happens, giving n=1 and option=123.
But: Suppose I enter 123.321 or 123#321.
Then the following happens:
* As the format string starts with a space, all leading whitespace is consumed. In this case, there is none.
* Then the 123 is consumed and put into option.
* As the format string is over now, parsing is stopped, n=1 and option=123.
* In the next loop run, there is no whitespace to skip. .321 resp. #321 is tried to be matched to %d, but these are no valid ints. Thus, we get n=0 and option keeps its old value.
* As no characters are consumed from the input stream (the one used is put back again), the same happens over and over again - that's why I put if (n <= 0) break;.
So you see that the behaviour has nothing to do with floating point, as it doesn't matter if we use . or # to "disturb".
We change our program to
#include <stdio.h>
int main(int argc, char ** argv)
{
while (1) {
int option; char c;
int n = scanf("%d%c", &option, &c);
printf("%d %d %d %c\n", n, option, c, c);
if (n <= 0) break;
}
}
and run it, inputting 4.235#6x7.
Then we get
* n=2, option=4 and c='.' at the first run
* n=2, option=235 and c='#' at the 2nd run
* n=2, option=6 and c='x' at the 3rd run
* n=2, option=7 and c='\n' (newline) at the 3rd run
and are prompted for further input.
This makes you open to the option
(parsed_inputs = scanf("%d%c", &option, &overcount_char)) < 1
and then check what overcount_char contains, whenever parsed_inputs is > 1.
I think you should put scanf() before the while loop and explicitly check "option" variable in the while loop.
What happening is that, here scanf() will always return the value 1, because scanf() returns no. of arguments read successfully. Hence this while loop will run forever. For further information check this link->http://www.cplusplus.com/reference/cstdio/scanf/
Hope this helps!

Resources