How do I restrict the user inputs to only numbers? [duplicate] - c

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()

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 to print a line from its beginning?

I've been coding a program to write data into a text file and practice data processes in c, and find data from there, every data is stored as lines. There are lines, and data is stored line by line, such as:
student name student surname student phone etc.
When i take an input of "student name" it starts to print without printing the name itself, prints what comes after it, same happens if i search for surname, only phone will be printed out.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
FILE *filePtr;
filePtr=fopen("std.txt","r");
char char_input[50];
char string[500];
printf("%s","Please give an input of the phone number\n");
scanf("%s",char_input);
while(!feof(filePtr)){
fscanf(filePtr,"%s",string);
if(strcmp(string, char_input)== 0){
fgets(string,500,filePtr);
puts(string);
}
}
fclose(filePtr);
}
Text file:
Andrew Brooks 865 965 55
Input:
Andrew
Output:
Brooks 865 965 55
Desired output:
Andrew Brooks 865 965 55
Instead of incorrectly using feof() and fscanf(filePtr,"%s", ... to incorrectly read a line. Use fgets() to read a line of the file and convert to a string.
Test the return value of fgets() to see if input occurred.
Use strstr() to look for a matching sub-string within string.
Example:
while (fgets(string, sizeof string, filePtr)) {
if (strstr(string, char_input)){
fputs(string, stdout);
}
}
The function feof will only tell you whether a previous input operation has already encountered end-of-file. It won't tell you whether you have now reached the end of file, so that the next input operation will fail. That function function is unable to predict whether the next input operation to fscanf or fgets will fail. Therefore, it should generally not be used as a loop condition. See this question for further information: Why is “while ( !feof (file) )” always wrong?
In your case, feof may return false and the subsequent function call to fscanf may return EOF due to encountering end-of-file. In that case, your posted code will ignore the return value of fscanf and behave as if fscanf had succeeded, and your posted code will attempt to process the non-existant input. This is likely to result in a bug.
Therefore, instead of using the function feof to determine whether the loop should be continued, you should check the return value of the input function.
You could rewrite your loop like this:
while ( fscanf(filePtr,"%s",string) == 1 ) {
if ( strcmp(string, char_input ) == 0 ) {
fgets( string, 500, filePtr );
puts( string );
}
}
This will solve the problem mentioned above of not checking the return value of fscanf. However, depending on the exact input, it may also be possible that the function fgets will fail due to encountering end-of-file. Therefore, it would be better if your program also checked the return value of the function fgets, instead of simply assuming that the function succeeded.
Another problem is that the line
puts(string);
will only print the contents of string, which is " Brooks 865 965 55". However, you also want to print "Andrew", which was read by the fscanf function call but has been meanwhile overwritten by the fgets function call. The simplest solution would be to print it before it gets overwritten. However, this will not work if the user searches for "Brooks" instead of "Andrew", because the word "Andrew" will already have been discarded in the previous loop iteration. This is because calling fscanf(filePtr,"%s",string) in a loop will not read one line of input per loop iteration, but will instead read a single word per loop iteration (which is not very meaningful).
Another consequence of reading in the input file word by word using fscanf(filePtr,"%s",string) is that your won't be able to find a match for the phone number "865 965 55". This is because your program will first read "865" from the input file and determine that this "word" is not identical to the search string. It will then read "965" and determine the same thing. It will do the same for "55".
The best solution would probably be to redesign your loop so that it always reads exactly one line of input per loop iteration, instead of only one word per loop iteration. After reading in one line of input, you can then parse the line by splitting it into "first name", "last name" and "phone number" using the function sscanf.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE *filePtr;
char search_string[50];
char line[200];
//open input file
filePtr = fopen( "std.txt", "r" );
if ( filePtr == NULL )
{
fprintf( stderr, "unable to open input file!\n" );
exit( EXIT_FAILURE );
}
//prompt user for input
printf( "Please enter search string: " );
//Note that the following code now uses "fgets" instead
//of "fscanf", because fscanf will only read a single
//word, when using the "%s" format specifier. This means
//that it would be unable to read the phone number
//"865 965 55" as an input string, because that line
//consists of three "words".
//read exactly one line of input from user
if ( fgets( search_string, sizeof search_string, stdin ) == NULL )
{
fprintf( stderr, "input failure!\n" );
exit( EXIT_FAILURE );
}
//remove newline character from input line by
//replacing it with terminating null character
search_string[strcspn(search_string,"\n")] = '\0';
//read exactly one line of input from the input file
//per loop iteration
while ( fgets( line, sizeof line, filePtr ) != NULL )
{
char first_name[50];
char last_name[50];
char phone_number[50];
//attempt to parse input
if (
sscanf(
line,
"%49s %49s %49[^\n]",
first_name,
last_name,
phone_number
)
!= 3
)
{
fprintf(
stderr,
"WARNING: skipping line due to parse error!\n"
);
continue;
}
//parsing was successful, so we can now search the
//3 individual fields for the search string
if (
strcmp( search_string, first_name ) == 0
||
strcmp( search_string, last_name ) == 0
||
strcmp( search_string, phone_number ) == 0
)
{
//remove newline character from input line by
//replacing it with terminating null character
line[strcspn(line,"\n")] = '\0';
//print entire input line of file for user
printf( "%s\n", line );
}
}
//cleanup
fclose(filePtr);
}
This program has the following behavior:
Please enter search string: Andrew
Andrew Brooks 865 965 55
Please enter search string: Brooks
Andrew Brooks 865 965 55
Please enter search string: 865 965 55
Andrew Brooks 865 965 55
Note that the code above is not perfect, as it has the following issues:
When using fgets, if the input line is too long to fit in the buffer, then the program will not detect this, although it should probably print an error message and quit, in such a situation.
If any of the fields "first name", "last name" or "phone number" is larger than 49 characters, the code does prevent a buffer overflow (which would possibly cause your program to crash), but it still doesn't handle this situation properly, for example by checking for such a situation and by printing an appropriate error message.
However, for your purposes, the code should probably be sufficient.
A more robust program, which fixes these issues, would be the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
//This function will read exactly one line of input using
//fgets and verify that the line was not too long for the
//input buffer. Note that the buffer size must be two bytes
//longer than the actual string length, because there must
//be space for the newline character and the terminating
//null character. The newline character will be overwritten
//with another terminating null character.
//On success, it will return true. If not further input is
//available due to end-of-file, it will return false.
//Otherwise, the function will not return, but will
//terminate the program with an error message.
bool get_one_line_of_user_input( char *buffer, int buffer_size )
{
char *p;
if ( fgets( buffer, buffer_size, stdin ) == NULL )
{
if ( feof( stdin ) )
{
return false;
}
else
{
fprintf( stderr, "input error!\n" );
exit( EXIT_FAILURE );
}
}
p = strchr( buffer, '\n' );
if ( p == NULL )
{
//No newline character was found. This could mean
//that the line was too long to store in the input
//buffer, in which case, the program should quit
//with an error message. However, it could also mean
//that input has been redirected to come from a
//file, and that this file ends with a line without
//a line ending. In that case, the missing newline
//character can be ignored.
if ( !feof( stdin ) )
{
fprintf( stderr, "line too long for buffer!\n" );
exit( EXIT_FAILURE );
}
}
else
{
//remove newline character
*p = '\0';
}
return true;
}
int main()
{
FILE *filePtr;
char search_string[50];
char line[200];
//open input file
filePtr = fopen( "std.txt", "r" );
if ( filePtr == NULL )
{
fprintf( stderr, "unable to open input file!\n" );
exit( EXIT_FAILURE );
}
//prompt user for input
printf( "Please enter search string: " );
//read exactly one line of input from user
if ( !get_one_line_of_user_input( search_string, sizeof search_string ) )
{
fprintf( stderr, "input failure!\n" );
exit( EXIT_FAILURE );
}
//read exactly one line of input from the input file
//per loop iteration
while ( get_one_line_of_user_input( line, sizeof line ) )
{
char first_name[50];
char last_name[50];
char phone_number[50];
//attempt to parse input
if (
sscanf(
line,
"%49s %49s %49[^\n]",
first_name,
last_name,
phone_number
)
!= 3
)
{
fprintf(
stderr,
"WARNING: skipping line due to parse error!\n"
);
continue;
}
//verify that none of the fields was too long
if (
strlen( first_name ) == 49
||
strlen( last_name ) == 49
||
strlen( phone_number ) == 49
)
{
//At least one buffer is full, and we have no way
//to determine whether the limit was exceeded or whether
//we are merely at the limit, so we must assume that
//the limit was exceeded.
fprintf(
stderr,
"WARNING: skipping line due to field length "
"limit exceeded!\n"
);
continue;
}
//parsing was successful, so we can now search the
//3 individual fields for the search string
if (
strcmp( search_string, first_name ) == 0
||
strcmp( search_string, last_name ) == 0
||
strcmp( search_string, phone_number ) == 0
)
{
//print entire input line of file for user
printf( "%s\n", line );
}
}
//cleanup
fclose(filePtr);
}

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.

Is there a way to count the number inputs from the user in C with 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" );
}

Resources