Good afternoon, my question is conceptual. How can I make it generate a "fancy" error when the user incorrectly enters some data that does not correspond to the scanf() function? So as to only allow integers to be entered in the example below (not characters or array of characters or an inappropriate data).
For example:
#include <stdio.h>
int a;
printf("Enter a number\n");
scanf("%d", &a); //the user is supposed to enter a number
printf("Your number is %d ", a);
//but if the user enters something inappropriate, like a character, the program leads to
//undetermined behavior (which as I understand it interprets said character according to its
//value in the ASCII code).
From already thank you very much
In order to determine whether scanf was able to successfully convert the input to an integer, you should check the return value of scanf:
#include <stdio.h>
#include <stdlib.h>
int main( void )
{
int num;
printf( "Enter a number: " );
if ( scanf( "%d", &num ) != 1 )
{
printf( "Failed to convert input!\n" );
exit( EXIT_FAILURE );
}
printf( "Conversion successful! The number is %d.\n", num );
}
However, using scanf for line-based user input is generally not recommended, because scanf does not behave in an intuitive manner when dealing with that kind of input. For example, scanf will generally not consume an entire line of input at once. Instead, it will generally only consume the input that matches the argument, but will leave the rest of the line on the input stream, including the newline character.
Leaving the newline character on the input stream can already cause a lot of trouble. For example, see this question.
Also, if the user enters for example 6abc, then scanf will successfully match the 6 and report success, but leave abc on the input stream, so that the next call to scanf will probably immediately fail.
For this reason, it is generally better to always read one line of input at a time, using the function fgets. After successfully reading one line of input as a string, you can use the function strtol to attempt to convert the string to an integer:
#include <stdio.h>
#include <stdlib.h>
int main( void )
{
char line[200], *p;
int num;
//prompt user for input
printf( "Enter a number: " );
//attempt to read one line of input
if ( fgets( line, sizeof line, stdin ) == NULL )
{
printf( "Input failure!\n" );
exit( EXIT_FAILURE );
}
//attempt to convert string to integer
num = strtol( line, &p, 10 );
if ( p == line )
{
printf( "Unable to convert to integer!\n" );
exit( EXIT_FAILURE );
}
//print result
printf( "Conversion successful! The number is %d.\n", num );
}
However, this code has the following issues:
It does not check whether the input line was too long to fit into the buffer.
It does not check whether the converted number is representable as an int, for example whether the number is too large to be stored in an int.
It will accept 6abc as valid input for the number 6. This is not as bad as scanf, because scanf will leave abc on the input stream, whereas fgets will not. However, it would probably still be better to reject the input instead of accepting it.
All of these issues can be solved by doing the following:
Issue #1 can be solved by checking
whether the input buffer contains a newline character, or
whether end-of-file has been reached, which can be treated as equivalent to a newline character, because it also indicates the end of the line.
Issue #2 can be solved by checking whether the function strtol set errno to the value of the macro constant ERANGE, to determine whether the converted value is representable as a long. In order to determine whether this value is also representable as an int, the value returned by strtol should be compared against INT_MIN and INT_MAX.
Issue #3 can be solved by checking all remaining characters on the line. Since strtol accepts leading whitespace characters, it would probably also be appropriate to accept trailing whitespace characters. However, if the input contains any other trailing characters, the input should probably be rejected.
Here is an improved version of the code, which solves all of the issues mentioned above and also puts everything into a function named get_int_from_user. This function will automatically reprompt the user for input, until the input is valid.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
int get_int_from_user( const char *prompt )
{
//loop forever until user enters a valid number
for (;;)
{
char buffer[1024], *p;
long l;
//prompt user for input
fputs( prompt, stdout );
//get one line of input from input stream
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
fprintf( stderr, "Unrecoverable input error!\n" );
exit( EXIT_FAILURE );
}
//make sure that entire line was read in (i.e. that
//the buffer was not too small)
if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
{
int c;
printf( "Line input was too long!\n" );
//discard remainder of line
do
{
c = getchar();
if ( c == EOF )
{
fprintf( stderr, "Unrecoverable error reading from input!\n" );
exit( EXIT_FAILURE );
}
} while ( c != '\n' );
continue;
}
//attempt to convert string to number
errno = 0;
l = strtol( buffer, &p, 10 );
if ( p == buffer )
{
printf( "Error converting string to number!\n" );
continue;
}
//make sure that number is representable as an "int"
if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
{
printf( "Number out of range error!\n" );
continue;
}
//make sure that remainder of line contains only whitespace,
//so that input such as "6abc" gets rejected
for ( ; *p != '\0'; p++ )
{
if ( !isspace( (unsigned char)*p ) )
{
printf( "Unexpected input encountered!\n" );
//cannot use `continue` here, because that would go to
//the next iteration of the innermost loop, but we
//want to go to the next iteration of the outer loop
goto continue_outer_loop;
}
}
return l;
continue_outer_loop:
continue;
}
}
int main( void )
{
int number;
number = get_int_from_user( "Enter a number: " );
printf( "Input was valid.\n" );
printf( "The number is: %d\n", number );
return 0;
}
This program has the following behavior:
Enter a number: abc
Error converting string to number!
Enter a number: 6000000000
Number out of range error!
Enter a number: 6 7 8
Unexpected input encountered!
Enter a number: 6abc
Unexpected input encountered!
Enter a number: 6
Input was valid.
The number is: 6
How to get verified user input of a specific type
#1 Get user input as a string
char s[100];
if (!fgets( s, sizeof(s), stdin )) *s = '\0';
char * p = strptok( s, "\r\n" );
if (!p) complain_and_quit();
*p = '\0';
...
Alternately:
#define __STDC_WANT_LIB_EXT2__ 1
#include <stdio.h>
char * s = NULL;
size_t n = 0;
if (getline( &s, &n, stdin ) < 0)
{
free( s );
complain_and_quit();
}
...
free( s );
#2 Get rid of any trailing whitespace
This could easily be put in a trim() function, but here we’ll spell it out:
Can’t believe I forgot this step. Sorry. 😳
p = strchr( s, '\0' );
while (p-- != s) if (!isspace( *p )) break;
p[1] = '\0';
#3 Try to convert that string to the type of thing you want.
char * p;
int user_input = strtol( s, &p, 10 );
if (*p)
{
// Input was not JUST an integer.
// It could be something like "123 xyz", or "not-an-integer".
// Look at the value of p to figure out where the conversion went wrong.
complain();
}
do_something_with_an_integer( user_input );
That’s it!
Related
I want to make sure that the character the user enters only works for a natural number.
I can do it for letters and symbols because it's a simple "else". But if the user enters a number that contains a decimal point, I want the program to stop with an error code. I know that it simply cuts off the decimal point if I read double into %d.
I generally do not recommend that you use scanf for user input, as that is not what the function was designed to do.
Your question is nearly identical to this question. The only difference is that the linked question asks about how to validate the user input as an integer, whereas you are asking about how to validate the user input as a natural integer, i.e. a positive or non-negative integer (depending on how you define natural number). Therefore, all you must do is use one of the answers from that question and add an additional check whether the number is positive or non-negative.
In the code below, I have modified the function get_int_from_user from my answer to the question mentioned above, by adding this additional check, and have changed the name of the function to get_natural_int_from_user.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
int get_natural_int_from_user( const char *prompt )
{
//loop forever until user enters a valid number
for (;;)
{
char buffer[1024], *p;
long l;
//prompt user for input
fputs( prompt, stdout );
//get one line of input from input stream
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
fprintf( stderr, "Unrecoverable input error!\n" );
exit( EXIT_FAILURE );
}
//make sure that entire line was read in (i.e. that
//the buffer was not too small)
if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
{
int c;
printf( "Line input was too long!\n" );
//discard remainder of line
do
{
c = getchar();
if ( c == EOF )
{
fprintf( stderr, "Unrecoverable error reading from input!\n" );
exit( EXIT_FAILURE );
}
} while ( c != '\n' );
continue;
}
//attempt to convert string to number
errno = 0;
l = strtol( buffer, &p, 10 );
if ( p == buffer )
{
printf( "Error converting string to number!\n" );
continue;
}
//make sure that number is representable as an "int"
if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
{
printf( "Number out of range error!\n" );
continue;
}
//make sure that number is non-negative
if ( l < 0 )
{
printf( "Natural numbers must be non-negative!\n" );
continue;
}
//make sure that remainder of line contains only whitespace,
//so that input such as "6sdfj23jlj" gets rejected
for ( ; *p != '\0'; p++ )
{
if ( !isspace( (unsigned char)*p ) )
{
printf( "Unexpected input encountered!\n" );
//cannot use `continue` here, because that would go to
//the next iteration of the innermost loop, but we
//want to go to the next iteration of the outer loop
goto continue_outer_loop;
}
}
return l;
continue_outer_loop:
continue;
}
}
int main( void )
{
int number;
number = get_natural_int_from_user( "Please enter a natural number: " );
printf( "Input was valid.\n" );
printf( "The number is: %d\n", number );
return 0;
}
This program has the following behavior:
Please enter a natural number: abc
Error converting string to number!
Please enter a natural number: 6abc
Unexpected input encountered!
Please enter a natural number: 5.7
Unexpected input encountered!
Please enter a natural number: -8
Natural numbers must be non-negative!
Please enter a natural number: 25
Input was valid.
The number is: 25
Note that in your question, you are asking for the program to abort if the input is invalid. However, this program will instead keep reprompting the user for input until it is valid. This behavior is generally more user-friendly.
So I need to read an integer from the stdin where the user may input 1.0, however since this is a double I wouldn't want to accept it. However when I try the method below the 1.0 is converted to 1 and is accepted. I would also like to accept 0001 as a possible integer input as 1.
first_sentence_to_switch = 0;
char buf[15]; // large enough
int number;
wrong_input = 0;
scanf("%14s", buf); // read everything we have in stdin
// printf("buffer: %s", buf);
if (sscanf(buf, "%d", &number) == 1)
{
first_sentence_to_switch = number;
}
else
{
wrong_input = 1;
}
You can use the %n format option to tell how much was matched by an sscanf call to make sure there is no extra cruft on the line:
if (sscanf(buf, "%d %n", &number, &end) == 1 && buf[end] == 0) {
.. ok
} else {
.. not an integer or something else in the input (besides whitespace) after the integer
Note the space between the %d and %n to skip any whitespace that might exist at the end of the buffer (such as a newline if the input was read by fgets or getline)
How to read a whole line of input
The line
scanf("%14s", buf);
will never read a whole line of input. It will only read a single word of input (which can also consist of digits). For example, if the user enters invalid input such as
"39 jdsuoew"
on a single line, then it will only read the word "39" as input, leaving the rest of the line on the input stream. This means that your program will accept the input as valid, although it should probably be rejected in this case.
Even if the user only entered "39", then it will only read this number, but will leave the newline character on the input stream, which can cause trouble.
If you want to ensure that it reads the entire line, I recommend that you use the function fgets instead, as that function will always read a whole line of input (including the newline character), assuming that the size of the provided memory buffer is large enough to store the entire line.
char line[100];
//attempt to read one line of input
if ( fgets( line, sizeof line, stdin ) == NULL )
{
fprintf( stderr, "Input error!\n" );
exit( EXIT_FAILURE );
}
//search for newline character, to verify that entire line was read in
if ( strchr( line, '\n' ) == NULL )
{
fprintf( stderr, "Line was too long for input buffer!\n" );
exit( EXIT_FAILURE );
}
Note that the function strchr requires that you #include <string.h>. If, as you state in the comments section, you are not allowed to use that header file, then you will probably have to assume that the memory buffer was large enough for the entire line, without verifying it (which you are also doing in your code). Although it is possible to verify this without using the function strchr, I don't recommend doing this. If the buffer is made large enough, then it is unlikely (but still possible) for the line to not fit into the buffer.
Convert string to integer using strtol
After reading the input line into a memory buffer, you can either use the function sscanf or strtol to attempt to convert the integer to a number. I recommend that you use the function strtol, because the function sscanf has undefined behavior if the user enters a number that is too large to be represented as a long int, whereas the function strtol is able to report such an error condition reliably.
In order to convert the line that you read to an integer, you could simply call strtol like this:
long l;
l = strtol( line, NULL, 10 );
However, calling the function with the second argument set to NULL has the same problem as calling the function atoi: You have no way of knowing whether the input was successfully converted, or if a conversion error occured. And you also have no way of knowing how much of the input was successfully converted, and whether the conversion failed prematurely, for example due to the user entering the decimal point of a floating-point number.
Therefore, it is better to call the function like this:
long l;
char *p;
l = strtol( line, &p, 10 );
Now, the pointer p will point to the first character that was not successfully converted to a number. In the ideal case, it will be pointing to the newline character at the end of the line (or maybe the terminating null character if you are not using fgets). So you could verify that the whole line was converted, and that at least one character was converted, like this:
if ( p == line || *p != '\n' )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
However, this is maybe a bit too strict. For example, if the user enters "39 " (with a space after the number), the input will be rejected. You probably would want to accept the input in this case. Therefore, instead of requiring that p is pointing to the newline character and thereby not accepting any other remaining characters on the line, you may want permit whitespace characters to remain in the line, like this:
if ( p == line )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
while ( *p != '\n' )
{
//verify that remaining character is whitespace character
if ( !isspace( (unsigned char)*p ) )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
p++;
}
Note that you must #include <ctype.h> in order to use the function isspace.
Also, as previously stated, the advantage of using the function strtol over sscanf is that it can reliably report whether the number is too large or too small to be representable as a long int. If such an error condition occurs, it will set errno to ERANGE. Note that you must #include <errno.h> in order to use errno.
long l;
char *p;
errno = 0; //make sure that errno is not already set to ERANGE
l = strtol( line, &p, 10 );
if ( errno == ERANGE )
{
printf( "Number out of range!\n" );
exit( EXIT_FAILURE );
}
Code example of fgets and strtol
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
int main( void )
{
char line[100], *p;
long l;
//prompt user for input
printf( "Please enter an integer: " );
//attempt to read one line of input
if ( fgets( line, sizeof line, stdin ) == NULL )
{
fprintf( stderr, "Input error!\n" );
exit( EXIT_FAILURE );
}
//search for newline character, to verify that entire line was read in
if ( strchr( line, '\n' ) == NULL )
{
fprintf( stderr, "Line was too long for input buffer!\n" );
exit( EXIT_FAILURE );
}
//make sure that errno is not already set to ERANGE
errno = 0;
//attempt to convert input to integer
l = strtol( line, &p, 10 );
//verify that conversion was successful
if ( p == line )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
//check for range error
if ( errno == ERANGE )
{
printf( "Number out of range!\n" );
exit( EXIT_FAILURE );
}
//verify that there are either no remaining characters, or that
//all remaining characters are whitespace characters
while ( *p != '\n' )
{
//verify that remaining character is whitespace character
if ( !isspace( (unsigned char)*p ) )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
p++;
}
//print valid input
printf( "Input is valid.\nYou entered: %ld\n", l );
}
This program has the following output:
Valid input:
Please enter an integer: 39
Input is valid.
You entered: 39
Junk after valid input on same line:
Please enter an integer: 39 jdsuoew
Error converting number!
Attempt to enter floating-point number instead of integer:
Please enter an integer: 1.0
Error converting number!
Attempt to enter number that is so large that it is not representable as a long int:
Please enter an integer: 10000000000000000000000000
Number out of range!
Since there could be a bunch of possible wrong inputs, you should probably look only for right ones: '1' and '0'.
'I would also like to accept 0001 ...'
I only assume from your explanation that you wouldn't want to accept something
like: 0011
I would look from the end of buffer towards beginning.
In another words:
I'd look only for single '1' at the end of buffer and then only for '0' (zeros)
until you reach the beginning of buf.
Everything else is a wrong input.
Since you arbitrarely choose buffer size, you could write something like:
#define BUFF_SZ 15
...
char buf[BUFF_SZ];
...
while (buf[++i]); // <-- to avoid measuring buffer size at runtime.
This is an example of code with a function that returns correct result:
#include <stdio.h>
int check_input (char *buf);
int main()
{
char buf[15]; // large enough
scanf("%14s", buf);
if (check_input(buf) == 0) { printf("Wrong input!"); return(1); };
... input OK ...
return (0);
}
// function returns: 1: on success, 0: on wrong input
int check_input (char *buf)
{
int i=0;
while (buf[++i]); // it will stop counting when NULL char is found ..
// so it's like 'strlen(buff)'
// without unnecessary including <string.h>
// buffer is set at end so check if it ends with '1' ..
if (buf[--i] != '1') return (0);
// now, check all buffer backwards to make sure that ALL of those are '0's..
while ((--i) > 0)
if (buf[i] != '0') return (0);
return (1);
}
I've written most important part as a funtion so it would be more readable.
I hope that could help.
#include <stdio.h>
#include <math.h>
int testprime (int x )
{
int i, root ;
if (x == 2) {
return 1 ;
}
if (x%2 == 0) {
return 0 ;
}
root = sqrt (x) ;
for (i = 3 ; i < root ; i ++) {
if (x%i == 0 ) {
return 0 ;
}
}
return 1 ;
}
int main ( )
{
int n , root ;
printf ("Enter the testing number\n") ;
//gets (n) ;
scanf ("%d", &n) ;
if (n <= 0) {
printf ("Error \n") ;
}
if (n == 1)
printf ("Unity number is %d\n",n) ;
if (0 == testprime(n) )
printf ("isn't a prime number\n") ;
else {
printf ("%d is a prime number \n",n) ;
}
return 0 ;
}
here, when I use scanf , I find the desired output . But when I use gets() function , it comes out as a garbage .. where is the problem ? I wrote the gets() function as a comment here to point where i am facing problem .
The old gets routine is no longer part of the standard C library. If you are using some other version of gets, such as something supplied for a programming course you are taking, we cannot tell you what the difference between that version and scanf is because we do not have the documentation for that version.
If you are using the old standard gets function, then a key difference is that scanf reads input and converts it to various types. Notably, for %d, scanf reads input and converts a decimal numeral in it to an int. So the corresponding argument is a pointer to an int where scanf will put the converted value. scanf("%d", &n); provides that pointer.
In contrast, gets reads characters and does not interpret them except to look for a newline character which will end the current input. It should be passed a pointer to an array of char where it will put the input.
That old standard gets is not suitable for reading a decimal numeral without further processing, and passing it the int n, as in gets(n), is wrong for two reasons. One, gets needs to know where to put the characters, so it needs a pointer, not an int. Two, gets should be passed a pointer to an array of char, not an int.
The returned value of gets is char*, and it takes a char[] buffer to write to as a parameter.
It can be (but shouldn't, as it's prone to buffer overflows) used to read a line in. In your case, I guess you could use it and then use something like atoi() or strtol() to read the first or more numbers from a line, but... I guess you were just mistaken about what it did.
The functions gets and fgets are used for reading a line of input from the user as a string. In contrast to scanf, it cannot read in a number directly. If you want to convert the string that it reads to a number, then you must do that in a separate step, for example by using the function strtol.
Although the simplest solution to read a number from the user is to use scanf with the %d format specifier, I don't recommend doing that, as that function does crazy things, because it is not designed for line-based input. For example, when the user enters "6sdfh4q", scanf accepts the "6" as valid input, but subsequent calls to scanf will fail, unless you remove the remainder of the line ("sdfh4q") from the input stream manually.
See this guide for more information on why not to use scanf:
A beginners' guide away from scanf()
The function gets was removed from the ISO C standard, because it was too dangerous to use. Therefore, for reading a string from the user, I recommend that you use the function fgets instead. Afterwards, you can use the function strtol to convert that string to a number, for example like this:
//the code below requires the following headers
#include <stdio.h>
#include <stdlib.h>
[...]
char buffer[100], *p;
long l;
//get one line of input from input stream
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
fprintf( stderr, "error reading input\n" );
}
else
{
//attempt to convert string to number
l = strtol( buffer, &p, 10 );
if ( p == buffer )
{
printf( "error converting string to number\n" );
}
else
{
//do something with the converted number
}
}
However, this code has the following problems:
It does not check whether the input buffer was large enough to contain the whole line of input.
It does not check whether the number the user entered is so high or low that it is not representable as a long.
The function strtol provided a long as the converted number, so additional range checks will be required if you want an int. You will have to make sure that the number is not too high or too low to be representable as an int.
If the user enters "6sdfh4q", it will consider this valid input of the number 6, because the first character of the line is a digit. It will ignore the rest of the line, although the entire input should probably be rejected in this case. It would probably be best to reject the input if it contains any non-whitespace characters after the converted digits.
The following code solves all of these problems.
In this code, I provide a function get_int_from_user with a simple interface. It takes one parameter which points to the string that the user should be prompted with (e.g. "Please enter a number: "). The function will repeat this prompt until the user enters a valid number. Once the user has entered a valid number, it will return that value as an int (provided that the number is not too high or low to be representable as an int).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
int get_int_from_user( const char *prompt )
{
for (;;) //loop forever until user enters a valid number
{
char buffer[1024], *p;
long l;
fputs( prompt, stdout );
//get one line of input from input stream
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
fprintf( stderr, "unrecoverable error reading from input\n" );
exit( EXIT_FAILURE );
}
//make sure that entire line was read in (i.e. that
//the buffer was not too small)
if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
{
int c;
printf( "line input was too long!\n" );
//discard remainder of line
do
{
c = getchar();
if ( c == EOF )
{
fprintf( stderr, "unrecoverable error reading from input\n" );
exit( EXIT_FAILURE );
}
} while ( c != '\n' );
continue;
}
//attempt to convert string to number
errno = 0;
l = strtol( buffer, &p, 10 );
if ( p == buffer )
{
printf( "error converting string to number\n" );
continue;
}
//make sure that number is representable as an "int"
if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
{
printf( "number out of range error\n" );
continue;
}
//make sure that remainder of line contains only whitespace,
//so that input such as "6sdfh4q" gets rejected
for ( ; *p != '\0'; p++ )
{
if ( !isspace( (unsigned char)*p ) )
{
printf( "unexpected input encountered!\n" );
//cannot use `continue` here, because that would go to
//the next iteration of the innermost loop, but we
//want to go to the next iteration of the outer loop
goto next_outer_loop_iteration;
}
}
return l;
next_outer_loop_iteration:
continue;
}
}
Instead of writing
printf ("Enter the testing number\n") ;
scanf ("%d", &n);
you can call the function get_int_from_user like this:
n = get_int_from_user( "Enter the testing number: " );
simply, gets() is used for char arrays (or strings).
using gets with n (which is an int) should not even let you compile the program.
If you really wanted to use gets() you could do something like:
#include <stdio.h>
#include <stdlib.h>
/* ... */
int main() {
char input[20]; //assuming input integer with less than 20 digits
gets(input);
int n = atoi(input), root;
/* ... */
}
edit: but as many people have pointed out, you really shouldn't
This question already has answers here:
Data Validation in C when the user inputs an incorrect value to avoid program crash
(3 answers)
Closed 1 year ago.
My program is to find the area of a triangle. How do I restrict user inputs to only numbers?
float a, b, c, area, s, dec; /*Declare variables*/
int m;
printf("Input value of side a : "); /*User input value of side a*/
scanf("%f", &a);
printf("Input value of side b : "); /*User input value of side b*/
scanf("%f", &b);
printf("Input value of side c : "); /*User input value of side c*/
scanf("%f", &c);
To see whether scanf was able to match the requested input, you must check the return value of scanf:
printf("Input value of side a : ");
if ( scanf("%f", &a) != 1 )
{
fprintf( stderr, "input error\n" );
exit( EXIT_FAILURE );
}
However, this code will accept a line of input of the form 3.45idfsgjs. In that case, scanf will match the 3.45 and not extract the rest of the line from the input stream.
If you want to validate the entire line of input, you can use fgets and strtof instead, as strtof has an optional paramter which will tell you how many characters were matched.
float a;
char line[100];
char *p;
printf("Input value of side a : ");
//attempt to read a line of input
if ( fgets( line, sizeof line, stdin ) == NULL )
{
fprintf( stderr, "input error\n" );
exit( EXIT_FAILURE );
}
//find newline character
p = strchr( line, '\n' );
//verify that newline character was found
//if not, then line was too long
if ( p == NULL )
{
fprintf( stderr, "line too long for buffer\n" );
exit( EXIT_FAILURE );
}
//remove newline character
*p = '\0';
//attemtpt to match input as floating-point number
a = strtof( line, &p );
//verify that entire line was matched
if ( *p != '\0' )
{
fprintf( stderr, "unable to match entire line\n" );
exit( EXIT_FAILURE );
}
//input was valid, the variable "a" now
//contains a valid number
Since this input validation code is rather long and must be called several times in your case, it would probably be appropriate to create a separate function which calls fgets and performs the input validation.
However, the above code has inconsistent behavior in that it accepts leading whitespace characters (which are discarded by strtof), but rejects trailing whitespace characters.
If you also want to reject leading whitespace characters, then you will have to call isspace on the first character, and reject the input if that function returns true.
If you want to instead accept trailing whitespace characters, then the lines
//verify that entire line was matched
if ( *p != '\0' )
{
fprintf( stderr, "unable to match entire line\n" );
exit( EXIT_FAILURE );
}
should be changed to:
//verify that at least one character was matched
if ( p == line )
{
fprintf( stderr, "unable to match number\n" );
exit( EXIT_FAILURE );
}
//verify that there are either no remaining characters
//or that all remaining characters are whitespace
while ( *p != '\0' )
{
if ( !isspace( (unsigned char)*p ) )
{
fprintf( stderr, "unable to match entire line\n" );
exit( EXIT_FAILURE );
}
p++;
}
Further reading:
A beginners' guide away from scanf()
I have the following block of code:
printf("Enter size:\n");
scanf("%d",&size);
My question is, how do I ensure that users only input one integer? I want the program to exit if they input an incorrect number of integers.
scanf returns the number of conversions and assignments, so
scanf( "%d", &size );
will return 1 if you successfully read an integer input (see below), 0 if you entered something that was not an integer (that is, the first non-whitespace character you type isn't a decimal digit), or EOF if there was an error on the input stream.
Here's where you need to be careful, though - the %d conversion specifier will skip leading whitespace, then read decimal digits until it sees any non-digit character (not just whitespace). So if you enter something like "12w45", scanf will convert and assign the 12 to size and return 1 to indicate success, even though you probably want to reject the entire input.
You either need to check the character immediately following your input like so:
int tmp;
char chk;
int n = 0;
if ( (n = scanf( "%d%c", &tmp, &chk )) == 2 ) // up to 2 conversions and assignments
{
if ( isspace( chk ) ) // only thing following your input is whitespace
size = tmp;
else
fprintf( stderr, "non-numeric character in input, try again\n" );
}
else if ( n == 1 ) // only thing following input was EOF
{
size = tmp;
}
else if ( n == 0 ) // first non-whitespace character was not a digit
{
fprintf( stderr, "non-numeric character in input, try again\n" );
}
else
{
fprintf( stderr, "Error on input stream\n" );
}
or you will need to use something other than scanf for validating input. You'd probably be better off reading the input as text using fgets, then converting that text using strtol (for integer types) or strtod (for floating point types) - they give you an opportunity to check for malformed input. Example:
char inbuf[12]; // up to 10 decimal digits plus sign and terminator
int size;
int tmp;
char *chk; // will point to the first character *not* converted by strtol
if ( fgets( inbuf, sizeof inbuf, stdin ) ) // read input as text
{
tmp = strtol( inbuf, &chk, 10 ); // convert string to integer
if ( !isspace( *chk ) && *chk != 0 )
{
fprintf( stderr, "non-numeric character in input, try again\n" );
}
else
{
size = tmp;
}
}
else
{
fprintf( stderr, "error on input\n" );
}