difference between gets ( ) and scanf () function - c

#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

Related

How can I make the number entered by the user to be a natural number only?

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.

scanf c wrong input

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!

How do you make a safe input of an int with retry logic?

I'm trying to write to a program that takes in an integer from the terminal and safely checks if it actually is an int and if it is equal to or in between 0 and 5. The code should also have retry logic.
This is the code that I have written:
#include <stdio.h>
#include <stdlib.h>
int main(){
int input;
char i[3];
char *p;
while(fgets(i, (sizeof(i)),stdin)){
input=strtol(i,&p,10);
if(input<0 || input>5 || p==i || (*p)!='\n'){
printf("please enter a integer larger or equal to 0 or smaller or equal to 5\n");
}
else{
printf("%d",input);
break;
}
}
}
The problem I have encountered is that if I input more characters than fgets() reads such as "aaaaa", I get the output:
please enter a integer larger or equal to 0 or smaller or equal to 5
please enter a integer larger or equal to 0 or smaller or equal to 5
please enter a integer larger or equal to 0 or smaller or equal to 5
I'm assuming the issue is that the characters that fgets does not read get left in the buffer and get read on subsequent iterations which trigger the if statement and therefore the repeated output. I'm assuming the solution is the somehow clear the buffer but from what I've read fflush() isn't allowed. Is there a simple way to clear the buffer? And how would you go about doing it, if that actually is the issue to the problem?
A simplistic approach starts with a bigger buffer.
#define BUF_N 100
char buf[BUF_N];
Then a not so simple approach that tests for all sort of troubles. I suspect simplifications exists, but at least this one handles many corners and is illustrative.
For simplicity, if the input line is excessively long, let us just say the input line is bad, regardless of text read.
#define BUF_N 100
char buf[BUF_N];
while (fgets(buf, sizeof buf, stdin)) {
// If entire buffer filled, the line may be longer, read it.
if (strlen(buf) == BUF_N - 1 && buf[BUF_N - 2] != '\n') {
printf("Excessive input\n");
// Consume rest of the line
int ch;
while ((ch = fgetc(stdin)) != '\n') {
if (ch == EOF) {
return EOF;
}
}
continue;
}
char *p;
long input = strtol(buf, &p, 10); // use long
if (buf == p) {
printf("Non-numeric input\n");
continue;
}
if (input < 0 || input > 5) {
printf("Out of range %ld\n", input);
continue;
}
// Look for trailing junk
while (isspace(*(unsigned char* )p)) {
p++;
}
if (*p) {
printf("Trailing junk\n");
continue;
}
return (int) input;
}
return EOF;
}
The above is not so tolerant of reading null characters.
Calling the function fgets with a buffer size of 3 will read at most one character, in addition to the newline character and the terminating null character. If the input is supposed to be a number between 0 and 5, then this behavior is acceptable, but you should be aware that your program will be unable to read input such as 05, even though it fulfills the range requirement.
If you want to allow input such as 05, then you should increase the buffer size.
You can easily detect whether the user entered more characters into the line than fit into the buffer. Since the function fgets also reads and stores the newline character into the buffer, you can check for the existance of this newline character, for example using the function strchr. If you determine that fgets did not read a newline character, then this means that the input was too long to fit into the buffer. In that case, you probably want to reject the input and discard the remainder of the input, so that it does not get read by the next call to fgets. This is what was happening in your program, which caused the same line to be processed multiple times.
One way of discarding the remainder of the input is to call getchar, which reads one character at a time. You should repeat this until you encounter the newline character, which means that the end of the line has been reached.
This is how the code could look:
while( fgets( i, sizeof i, stdin ) != NULL )
{
//verify that entire line of input was read
if ( strchr( i, '\n' ) == NULL )
{
int c;
//verify that stream state is as expected
if ( feof(stdin) || ferror(stdin) )
{
fprintf( stderr, "unexpected input error!\n" );
exit( EXIT_FAILURE );
}
//print error message
printf( "Line input was too long to fit into buffer!\n" );
//discard remainder of line
do
{
c = getchar();
if ( c == EOF )
{
fprintf( stderr, "unexpected input error!\n" );
exit( EXIT_FAILURE );
}
} while ( c != '\n' );
continue;
}
//input was successful, so we can now process it with strtol
}
Note that you must additionally #include <string.h> in order to use the function strchr.

How to not count 1.0 as an integer

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.

While with multiple conditions

This is my code.The user enters a string and if the input is 1,2 or * it displays error.The problem is that the condition is satisfied for all the numbers of which the first digits are 1,2 amd 3.(11,12,22...).I used also strlen and sizeof function to count the digits and create a condition that works but when i printed strlen the output was 1 for every number.
I don't understand if the problem is my multiple conditions or the use of scanf
Here is my code
#include < stdio.h >
int main (void)
{
char choice;
int i=0;
do
{
scanf("%s", &choice);
while(choice !='1' && choice !='2' && choice !='*' )
{
printf("error");
scanf("%s",&choice);
}
}while (choice !='0');
return 0;
}
I would be very thankful for any help. I am quite new in programming, so forgive me if the question is stupid.
I propose you the following code:
#include <stdio.h>
#include <string.h>
int main (void)
{
char choice[201];
do
{
scanf("%200s", choice);
if (strcmp(choice, "1") == 0 ||
strcmp(choice, "2") == 0 ||
strcmp(choice, "3") == 0)
printf("error");
} while (strcmp(choice, "0") != 0);
return 0;
}
%s waits for a char * argument, your &choice was right, but scanf will write a whole string at the address pointed to, which can (will) contain more than one char. By giving the address of a char variable, you provided it with the room for only one char.
You can't compare strings with a comparison operator, you have to use, for example, the strcmp function, which returns 0 if it's two argument do match and non-zero otherwise.
If I properly understood your intent, there is no need for two nested while loop. The two scanf calls aren't needed either.
The 200 in the %200s scanf format string, limits the number of chars which will be written in the array you have provided the address of.
This array is of size 201 to account for the extra '\0' byte, terminating C strings.
To write properly this code, one should test the return of scanf, to check if something has been parsed with success.
Please refer to the man page of scanf to see how to check if the parsing went successfully.
You've declared choice as a char object - it's only large enough to store a single character value. If you're entering a string like "123" on input, only the leading '1' is stored to choice and the remaining input is written to memory immediately following choice, which may cause a runtime error if you clobber anything important.
If you want to read and store the input as a string of characters, then you need to declare choice as an array of char, sized to hold at least one more character than the maximum input size - IOW, if you expect the maximum string to be 3 characters long, then choice must be declared as char choice[4];.
If you want to read and store the input as an integer, then you must declare choice as int instead of char, and you need to use the %d conversion specifier instead of %s.
Consider using fgets to obtain input, then parse with sscanf. Using int choice allows you to distinguish between 1 and 11...
#include <stdio.h>
int main( void) {
char input[99] = "";
int choice = 0;
do {
printf ( "Enter 0 to quit\n");
if ( fgets ( input, sizeof input, stdin)) {
if ( 1 == sscanf ( input, "%d", &choice)) {
if ( choice == 1 || choice == 2 || choice == 3) {
printf ( "error\n");
}
else {
printf ( "you entered %d\n", choice);
}
}
else {
if ( 0 == strcmp ( input, "*\n")) {
printf ( "error\n");
}
//sscanf could not parse an int
choice = -1;
}
}
else {
break;// fgets failed
}
} while ( choice != 0);
return 0;
}
the following proposed code:
cleanly compiles
only calls scanf() once per each outer loop
prompts the user for the desired input
and now the code:
#include <stdio.h> // scanf(), printf(), perror()
#include <stdlib.h> // exit(), EXIT_FAILURE
int main (void)
{
char choice;
do
{
printf( "enter 0:" );
if( 1 != scanf(" %c", &choice) )
{
perror( "scanf failed" ); // to stderr:
// enclosed text + reason OS thinks it failed
exit( EXIT_FAILURE ); // once failed, it will continue to fail
// so exit program with error indication
}
// implied else, scanf successful
// what about other user inputs?
if(choice !='1' && choice !='2' && choice !='*' )
{
printf("error\n"); // trailing '\n'
// so immediately displayed on terminal
// note: should use: 'fprintf( stderr, ... )'
// because error messages should
// be output to 'stderr', not 'stdout'
}
} while (choice !='0');
return 0;
} // end function: main

Resources