I'm trying to write a program that reads a five digit zip code, then outputs the numbers in word form. The program would also only accept a 5 digit number. So if the input was 123, the output would be "You entered 3 digits" and loop back until the user entered five digits. Once they did, it would output the number in word format. So if 12345 was entered, the output would be one two three four five. This is what I have so far:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>
int main(void) {
int ac, count = 0;
printf("Enter a 5 digit area code: ");
scanf("%d", &ac);
do {
ac /= 10;
++count;
} while (ac != 0);
while (count != 5) {
printf("You intered in %d digits.\n", count);
printf("Enter a 5 digit area code: ");
scanf(" %d", &ac);
}
while (count == 5) {
}
return(0);
}
There are two issues I need help with. The first is that whenever a number that doesn't have five digits is input, it outputs correctly once, and then the loop is infinite. If I entered 123, output would be "You entered 3 digits," and then it would prompt me to enter another number. If I then entered 12345 it would output "You entered 3 digits" again. I'm new to loops not sure where this problem is coming from.
The second issue is just the conversion from numbers to words, I've never encountered this issue and I'm not sure where to start.
As already pointed out in the comments section, the logic in your code will not work with codes that start with a zero.
If you store the code as an integer, then you cannot distinguish the code 01234 from the code 1234. Therefore, if you want to be able to distinguish these two codes, you must, at least initially, read the number as a string, not as a number:
char buffer[100];
//prompt user for input
printf("Enter a 5 digit area code: ");
//attempt to read one line of input
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
fprintf( stderr, "error reading input!\n" );
exit( EXIT_FAILURE );
}
Note that the code above additionally requires the header file stdlib.h to be included.
Now, you must count the number of characters entered and verify that there were exactly 5. Also, you must verify that all entered characters were digits. If the input is not valid, then you must provide an appropriate error message and prompt the user again. This can be best accomplished using an infinite loop, which is repeated until the input is valid, at which point an explicit break statement is executed, which will break out of the infinite loop.
Converting the individual digits to words can easily be done by creating an array of pointers to strings, so that the 0th element points to the string "zero", the 1st element points to "one", etc:
const char * const digit_names[] = {
"zero", "one", "two", "three", "four",
"five", "six", "seven", "eight", "nine"
};
Since the ISO C standard guarantees that all numbers are consecutive in the character set (irrespective of which character set you are using), you can simply subtract '0' (the character code of the digit 0) from the character code in order to convert the digit from a character code to an actual number between 0 and 9. Now, you can use that number to index into the array digit_names to print the corresponding word.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main( void )
{
const char * const digit_names[] = {
"zero", "one", "two", "three", "four",
"five", "six", "seven", "eight", "nine"
};
char buffer[100];
for (;;) //infinite loop, equivalent to while(1)
{
int i;
char *p;
//prompt user for input
printf("Enter a 5 digit area code: ");
//attempt to read one line of input
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
fprintf( stderr, "Unrecoverable error reading input!\n" );
exit( EXIT_FAILURE );
}
//verify that entire line of input was read in
p = strchr( buffer, '\n' );
if ( p == NULL )
{
fprintf( stderr, "Unrecoverable error: Line too long!\n" );
exit( EXIT_FAILURE );
}
//remove newline character
*p = '\0';
//verify that exactly 5 characters were entered and that
//each characters is a digit
for ( i = 0; i < 5; i++ )
{
//verify that we are not yet at end of line
if ( buffer[i] == '\0' )
{
printf( "Too few characters!\n" );
//we cannot use "continue" here, because that would apply to
//the innermost loop, but we want to continue to the next
//iteration of the outer loop
goto continue_outer_loop;
}
//verify that character is digit
if ( !isdigit( (unsigned char)buffer[i] ) )
{
printf( "Only digits allowed!\n" );
//we cannot use "continue" here, because that would apply to
//the innermost loop, but we want to continue to the next
//iteration of the outer loop
goto continue_outer_loop;
}
}
//verify that we are now at end of line
if ( buffer[i] != '\0' )
{
printf( "Too many characters!\n" );
continue;
}
//everything is ok with user input, so we can break loop
break;
continue_outer_loop:
continue;
}
printf( "You entered this valid input: %s\n", buffer );
printf( "In words, that is: " );
for ( int i = 0; i < 5; i++ )
{
//don't print space on first iteration
if ( i != 0 )
putchar( ' ' );
//casting to "unsigned char" is necessary to prevent
//negative character codes
fputs( digit_names[ ((unsigned char)buffer[i]) - '0' ], stdout );
}
printf( "\n" );
}
Here is some sample input and output from the program:
Enter a 5 digit area code: 1234
Too few characters!
Enter a 5 digit area code: 123456
Too many characters!
Enter a 5 digit area code: 12h45
Only digits allowed!
Enter a 5 digit area code: 12345
You entered this valid input: 12345
In words, that is: one two three four five
Also, as you can see, codes starting with 0 work, too:
Enter a 5 digit area code: 1234
Too few characters!
Enter a 5 digit area code: 01234
You entered this valid input: 01234
In words, that is: zero one two three four
For the first issue, the second while loop condition always evaluates false if the number is not 5 digits. You have to update count each time you get a new number, this means you have to repeat the do ... while loop. A function will be great in this case, and never forget to check the return value of scanf() is the only you know the conversion succeeded (you scanned a number).
Your function can be something like this,
int count_digits(int num)
{
int digits = 0;
do
{
digits++;
num /= 10;
} while (num != 0);
return digits;
}
and your updated main()
int main(void)
{
int ac = 0, count = 0;
printf("Enter a 5 digit area code: ");
if (scanf("%d", &ac) != 1) /* you read one int */
{
/* conversion error - maybe display error message */
exit(EXIT_FAILURE);
}
count = count_digits(ac);
while (count != 5)
{
printf("You intered in %d digits.\n", count);
printf("Enter a 5 digit area code: ");
if (scanf(" %d", &ac) != 1)
{
exit(EXIT_FAILURE); /* include <stdlib.h> for exit */
}
count = count_digits(ac);
}
}
As for the second part of your question, its not clear if you want to perform some operations on the number. If you need to have it as a int, you may use snprintf() and large enough buffer to convert it to a string.
char buff[100] = "";
snprintf(buff, sizeof(buff), "%d", ac);
If you don't need to perform any operations on the number, you can read it as string right away, with fgets().
char buff[100] = "";
fgets(buff, sizeof(buff), stdin);
(This will also work for zip codes starting with 0)
As ZIP codes can begin with a '0', using scan("%d",... loses leading 0 information.
Code needs to detect digits and not just an int to disallow input like "123\n", "000123\n", "+1234\n", "12345xyz\n", yet allow "000001\n", " 000001\n", "000001 \n", "000001".
Below uses "%n" to record the offset of the scan.
char buf[100];
if (fgets(buf, sizeof buf, stdin)) {
int left, right, end;
long zip;
if (sscanf(buf, " %n%6ld%n %n", &left, &zip, &right, &end) != 1) {
puts("Non-numeric input");
} else if (right - left != 5 || !isdigit((unsigned char ) buf[left])) {
puts("Not 5 digits");
} else if (buf[end] != '\0') {
puts("extra junk at the end");
} else {
printf("Success %05ld\n", zip);
}
}
Alternative code may also want to disallow leading/trailing white-space.
" %n%6ld%n %n" details
" ": Consume optional white-space.
"%n" Record offset of scan
"%6ld" Read upto 6 characters in forming a long - no need for more (and prevent overflow). As int may be 16-bit, use long for 00000-99999.
Sometimes it helps to simplify.
In this case eliminating one while loop and using a helper function in the remaining loop will help. The following enables the code to iterate until the right input is entered with a single loop. (note, example contains no error handling, just concept illustration.)
The main:
int main(void)
{
int num;
char buf[20]={0};
printf("Enter a 5 digit number:");
while (!isNumeric(buf))//helper function
{
printf("try again\n:");
scanf("%s", buf);
}
//convert if needed
num = atoi(buf);
printf("%s\n%d", buf, num);
return 0;
}
The helper function:
bool isNumeric(char *number)
{
if(strlen(number) != 5) return false;
while(*number)
{
if(!isdigit(*number))
return false;
number++;
}
return true;
}
Related
I have this program:
#include <stdio.h>
int main(){
int val;
printf("Enter any number: ");
scanf("%d",&val);
printf("The number incremented is %d\n",val+1);
printf("Enter any number again: ");
scanf("%d",&val);
printf("The number decremented is: %d\n",val-1);
return 0;
}
It works completely fine if i give it an integer input, but when its a different input, the program goes crazy and runs without accepting the second input. (the second output is -1). is there a fix to this?
I have tried to test the program with number and non-number inputs, For the non number one in the first input, as the description said, the program continued and didnt accept the second input.
Edit: I did not mention the desired output of the program, sorry about that.
If the output is non-integer then it will return with err code 1.
If you want the function to return -1; in the case of the user entering invalid input, then you should check the return value of scanf, for example like this:
#include <stdio.h>
int main( void )
{
int val;
printf( "Enter any number: ");
if ( scanf( "%d", &val ) != 1 )
{
printf( "Invalid input!\n" );
return -1;
}
printf( "The number incremented is %d\n", val+1 );
printf( "Enter any number again: " );
if ( scanf( "%d", &val ) != 1 )
{
printf( "Invalid input!\n" );
return -1;
}
printf( "The number decremented is: %d\n", val-1 );
return 0;
}
This program has the following behavior:
Enter any number: abc
Invalid input!
Enter any number: 5
The number incremented is 6
Enter any number again: abc
Invalid input!
Enter any number: 5
The number incremented is 6
Enter any number again: 10
The number decremented is: 9
However, this solution is not perfect. For example, if the user enters 5abc in the first line, then the first scanf successfully reads 5, but the second scanf will fail:
Enter any number: 5abc
The number incremented is 6
Enter any number again: Invalid input!
If you don't want this counter-intuitive behavior, then it would probably be best not to use the function scanf for line-based user input, but to rather use the functions fgets and strtol instead, for example like this:
#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 );
int main( void )
{
int val;
val = get_int_from_user( "Enter any number: ");
printf( "The number incremented is %d\n", val+1 );
val = get_int_from_user( "Enter any number again: ");
printf( "The number decremented is: %d\n", val-1 );
return 0;
}
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 "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;
}
}
This program has the following behavior:
Enter any number: 5abc
Unexpected input encountered!
Enter any number: abc
Error converting string to number!
Enter any number: 5
The number incremented is 6
Enter any number again: 10
The number decremented is: 9
Note that my second program is more sophisticated than the first program, because it keeps on prompting the user for input until the user enters a valid int, whereas the first program simply prints an error message and returns -1.
I took the function get_int_from_user from this answer of mine to another question. See that answer for more information on the extensive input validation that the function performs.
The first scanf is looking for an integer if it doesn't get an integer it does nothing,
The secoind scanf is looking for an integer if it doesn't get an integer it does nothing.
If the first scanf does nothing, nothing changes, so the second identical scanf presented with the same environment will also do nothing.
one way to fix this is to tell the second scanf to discard the garbage characters.
scanf("%*[^0-9]%d",&val);
Subsequent edits to the question made that solution unsuitable.
#include <stdio.h>
int main(){
int val;
char junk[2];
printf("Enter any number: ");
if( scanf("%d%1[\n]",&val,&(junk[0])) < 2)
{
puts("numbers only please");
return 1;
}
printf("The number incremented is %d\n",val+1);
printf("Enter any number again: ");
if( scanf("%d%1[\n]",&val,&(junk[0])) < 2)
{
puts("numbers only please");
return 1;
}
printf("The number decremented is: %d\n",val-1);
return 0;
}
I am just a beginner to C language. I am trying to write a programme that take marks of four subjects. But in case if user provides a wrong input such as alphabets then it should print "Please Enter Number" and should again ask the marks of same subject.
Here is my code..
// Programme to add marks of four subject and to calculate percentage.
#include <stdio.h>
#include<ctype.h>
int main()
{
float sub_marks, total_marks, temp, check = 0;
printf("\nPragramme to add marks of four subject and to calculate percentage.\n\n");
for (int i = 1; i <= 4; i++) //Running loop four times to enter marks of four subjects.
{
printf("Enter Marks of Subject %d: ", i);
scanf("%f", &sub_marks);
if (sub_marks > 100) //condition for what to do if marks are greater then 100
{
printf("\n**MARKS CONNOT BE GREATER THEN 100**\n\n");
i--;
}
else
{
temp = temp + sub_marks; //addind marks to get total marks
}
}
printf("\n\nTotal Marks: 400");
printf("\n\nObtained marks: %.2f", temp);
printf("\n\nPercentage: %.2f%%\n\n", temp / 4);
return 0;
}
I did try a lot but ended up with output..
Pragrame to add marks of fout subject and to calculate percentage.
Enter Marks of Subject 1: 65
Enter Marks of Subject 2: y
**PLEASE ENTER NUMBER.**
Enter Marks of Subject 3:
**PLEASE ENTER NUMBER.**
Enter Marks of Subject 4:
**PLEASE ENTER NUMBER.**
After entering alphabet it dosen't let the user to input for the rest of the loops. Instead it should ask "Enter Marks of Subject 1:"
I achived above output by placing below code after else.
while (sub_marks >= 0)
{
remainder = sub_marks % 10;
if (!isdigit(remainder))
{
printf("Please enter Number");
break;
}
}
Unfortunately, the C standard library does not offer any easy way to read a floating-point number from the user, and to automatically keep asking the user until the input is valid. However, you can write such a function yourself, using the functions fgets and strtof.
In the code snippet below, I wrote a variadic function
float get_float_from_user( const char *prompt, ... )
which you can call like printf to print a string. It will repeatedly prompt the user for input using this string, until the input is valid. Once the input is valid, the function will return the user input converted to a float. You can call the function for example like this:
sub_marks = get_float_from_user( "Enter Marks of Subject %d: ", i );
If you replace the call to scanf with the line above, then your program should work as desired, after additionally fixing the following bug:
You must initialize temp to 0. The line
float sub_marks, total_marks, temp, check = 0;
will not initialize temp to 0. It will only initialize check to 0. If you want to initialize all 4 variables to 0, then you should instead write the following:
float sub_marks = 0, total_marks = 0, temp = 0, check = 0;
However, it would probably be better to change the name of temp to sum, as that describes the purpose of the variable better. Also you are not using the variables total_marks and check at all, so you can remove them. Therefore, you may want to change that line to the following:
float sub_marks, sum = 0;
Note that I am deliberately not initializing sub_marks, as that is not necessary (initializing sum is necessary, though).
However, since you are not using sub_marks outside the loop, it would probably be better to declare it inside the loop, in order to limit its scope.
Also, changing the loop counter i inside the loop is considered bad programming practice. A cleaner solution would be to create an additional loop inside the loop, so that the inner loop will only stop when the input is in the desired range.
Here is the code which does everything mentioned above:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
float get_float_from_user( const char *prompt, ... )
{
for (;;) //loop forever until user enters a valid number
{
char buffer[1024], *p;
float f;
va_list vl;
//prompt user for input
va_start( vl, prompt );
vprintf( prompt, vl );
va_end( vl );
//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;
f = strtof( buffer, &p );
if ( p == buffer )
{
printf( "error converting string to number\n" );
continue;
}
//make sure that no range error occurred
if ( errno == ERANGE )
{
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 continue_outer_loop;
}
}
return f;
continue_outer_loop:
continue;
}
}
int main()
{
float sum = 0;
printf( "\nProgram to add marks of four subjects and to calculate average.\n\n" );
for ( int i = 1; i <= 4; i++ )
{
float sub_marks;
//loop forever until input is in the desired range
for (;;)
{
sub_marks = get_float_from_user( "Enter marks of subject %d: ", i );
if ( sub_marks < 0.0 )
{
printf( "Marks cannot be negative!\n" );
continue;
}
if ( sub_marks > 100.0 )
{
printf( "Marks cannot be greater than 100!\n" );
continue;
}
//input is in acceptable range, so break out of infinite loop
break;
}
sum += sub_marks;
}
printf( "\n" );
printf( "Total marks: 400\n" );
printf( "Obtained marks: %.2f\n", sum);
printf( "Average: %.2f%%\n", sum / 4.0 );
printf( "\n" );
return 0;
}
The program above has the following behavior:
Program to add marks of four subjects and to calculate average.
Enter marks of subject 1: This is a test.
error converting string to number
Enter marks of subject 1: 70
Enter marks of subject 2: 80abc
unexpected input encountered!
Enter marks of subject 2: 80
Enter marks of subject 3: 110
Marks cannot be greater than 100!
Enter marks of subject 3: 90.7
Enter marks of subject 4: abc85
error converting string to number
Enter marks of subject 4: 85
Total marks: 400
Obtained marks: 325.70
Average: 81.43%
The function get_float_from_user is a slight modification of my function get_int_from_user from the second code snippet of this answer of mine to another question.
I've tried to put everything together as much as possible, but it's hard to read input correctly, managing everything that users can throw at you. This version is not perfect!
EDIT: As Andreas Wenzel pointed out: This solution has the following issues:
If the user enters a line that is so long that it doesn't fit into the input buffer, then your program won't handle the input properly. Ideally, you should check for the newline character in the input buffer.
The function sscanf has undefined behavior if the input is out of range of a float. This cannot be prevented when using the function sscanf. Therefore, it is generally not a good idea to use that function for input validation. The function strtof is better, as the behavior is well-defined for an out of range error.
The main thing is that you were computing the "average", not the "percentage".
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int main(void)
{
printf("Program to add marks of four subjects and to calculate the average.\n\n");
float total_marks = 0;
for (int i = 1; i <= 4; i++) { // Running loop four times to enter marks of four subjects.
bool valid = false;
while (!valid) {
printf("Enter mark of subject %d: ", i);
char line[256];
if (fgets(line, 256, stdin) == NULL) {
printf("\n**INPUT ERROR OR EOF**\n\n");
exit(EXIT_FAILURE);
}
float mark;
int n;
if (sscanf(line, "%f %n", &mark, &n) != 1) {
printf("\n**ENTER A FLOATING POINT VALUE**\n\n");
}
else if (line[n] != 0) {
printf("\n**DON'T ADD ANYTHING AFTER THE NUMBER**\n\n");
}
else if (mark < 0 || mark > 100) {
printf("\n**MARKS MUST BE BETWEEN 0 AND 100**\n\n");
}
else {
valid = true;
total_marks += mark; // add marks to get total
}
}
}
printf("Total Marks: %.2f\n", total_marks);
printf("Average: %.2f\n\n", total_marks / 4);
return EXIT_SUCCESS;
}
Here's my code. When it asked for my first input on the command line, I entered '5cde'. Then it printed '5', and it didn't ask for any more inputs before termination.
How is this okay? Is there a way to see how scanf is working internally as I run this program? I really want to know what exactly is going on behind the scenes. I'm not sure how the input buffer works, and if I should even be messing with it. Or should I just use a different io function instead? Because scanf seems pretty unsafe to me.
int i;
scanf("%d", &i);
printf("%d", i);
scanf("%d", &i);
scanf("%d", &i);
scanf("%d", &i);
scanf("%d", &i);
The behavior of the function scanf when the conversion specifier d is used is the following
d Matches an optionally signed decimal integer, whose format is the same as expected for the subject sequence of the strtol function
with the value 10 for the base argument. The corresponding argument
shall be a pointer to signed integer
So for this input buffer
5cde
the first call of scanf reads the character 5 because it represents a valid integer number and the position in the input buffer moves to the right to the character 'c'.
When the next and subsequent calls of scanf failed because the symbol 'c' is not a valid digit. The position in the input stream stays the same.
You should check whether call of scanf was successful> For example
if ( scanf("%d", &i) == 1 )
{
printf("%d\n", i);
}
else
{
// remove the invalid input
scanf( "%*[^\n]%*c" );
}
Here is a demonstration program.
#include <stdio.h>
int main( void )
{
enum { N = 3 };
printf( "Enter %d numbers.", N );
for ( int i = 0; i < N; i++ )
{
printf( "\t%d: ", i + 1 );
int value;
while ( !( scanf( "%d", &value ) == 1 ) )
{
scanf( "%*[^\n]%*c" );
puts( "Invalid input. Try again" );
printf( "\t%d: ", i + 1 );
}
printf( "You inputted %d\n", value );
}
}
Its output might look like
Enter 3 numbers.
1: 1A
You inputted 1
2:
Invalid input. Try again
2: B
Invalid input. Try again
2: 2C
You inputted 2
3:
Invalid input. Try again
3: 3
You inputted 3
That is the user entered the following sequence of strings
1A
B
2C
3
You should check the result of each scanf call. It returns the number of items successfully read (or EOF in case of failure) so in your case it should return 1 or otherwise something went wrong.
Please also note that each time you hit enter during input, there's a trailing line feed character added to stdin which needs to be discarded, if you were for example to read a string after reading a number. This should be addressed early on in your C book, if it's any good.
Overall scanf is to be regarded as a quick & dirty function for simplistic programs and not something to be used in real production code, so you don't really need to worry about leaning all the details of it. Using fgets and reading everything as strings is much better (and faster) practice.
Every time you ask for input, the user will also press Enter.
Therefore, get input as a string, then turn that string into a number. As there are several steps to reading a number and verifying it as correct, you can even write a convenient function to help.
void fatal_error( const char * message )
{
fprintf( stderr, "%s\n", message );
exit( 1 );
}
int get_int( const char * prompt, int min, int max, const char * error_message )
{
char s[ 100 ];
int n, count;
printf( "%s ", prompt );
fflush( stdout );
if ( !fgets( s, sizeof(s), stdin ) // user's input
or (sscanf_s( s, "%d %n", &n, &count ) != 1) // convert to int
or (count != strlen( s )) // no "123abc" stuff allowed
or (n < min)
or (n > max) )
fatal_error( error_message );
return n;
}
int main()
{
int n = ask_int( "n?", 1, 100, "n is not optional, and must be in [1,100]" );
int age = ask_int( "How old are you?", 0, INT_MAX, "You cannot be negative years old." );
etc
How I can exclude characters from being entered as a value in my programs?
Is there a way through the scanf function to recognize the input as a character and then write a printf to show an invalid value message? It would be more recognizing the character then printing the message I'm concerned with.
Edit:
So as asked, the below is my code for a program that first reads five numbers(each between 1 and 30).For each number read, the program should print a line containing that number of adjacent asterisks.
For this, if I enter a number value it causes the program to stop working. So if i could add a way to create "Try again" message or something similar when they are entered, this will stop it from having errors.
#include <stdio.h>
int main(void)
{
int number1 = 0; int counter;
int sentinelcount = 1;
printf("Please enter 5 values, between 1 and 30");
while (sentinelcount <= 5) {
printf("\n\nEnter number: \n"); /*prompt*/
scanf_s("%d", &number1); /*read an interger*/
sentinelcount++;
if (number1 < 1 || number1 > 30)
{
sentinelcount--;
printf("\nWrong Value\n");
}
if (number1 < 1 || number1 > 30)
{
printf("Enter within correct value range: 1 - 30! ");
}
else if (number1 >= 1 || number1 <= 30)
{
printf("Number of asterisks:\n");
for (counter = 1; counter <= number1;
counter++)
{
printf("*");
}
}
}
return 0;
How do I stop characters from being entered into program
Short of some magic hand that prevents the user from typing in non-numeric or a limited key board, the better approach is not to stop characters from being entered, but accept them as input and then detect invalid input.
I recommend to consume invalid input and alert the user of the issue.
A good first attempt is to read a line of input into a string with fgets().
Test for input, conversion success, extra junk and range.
char buf[80];
if (fgets(buf, sizeof buf, stdin)) { // TBD deal with lines longer than 79
If a line was read, process it with strtol(), sscanf(), etc. Use "%n" to detect where scanning ended. Perform error checking.
int num;
int n;
// If an integer was parsed with no trailing junk and in range ...
if (sscanf(buf, "%4d %n", &num, &n) == 1 && buf[n] == 0 &&
(num >= 1 && num <= 30)) {
Oh_Happy_Day(); // TBD code
} else {
Invalid_input(): // TBD code
}
One way to determine whether the user entered a character or a number is to call scanf with the %d conversion format specifier, and check the return value of scanf. If it returns 1, then the conversion format specifier was successfully matched. Otherwise, you print an error message and prompt the user again to enter input. For example:
#include <stdio.h>
int main( void )
{
int i;
//infinite loop, equivalent to while(1)
//repeat the loop until the input is valid
for (;;)
{
//prompt user for input
printf( "Please enter a number: " );
//attempt to read and convert user input
if ( scanf( "%d", &i ) == 1 )
{
//input was valid
break;
}
//print error message
printf( "Input was invalid, please try again!\n" );
//discard remainder of line
for ( int c; c = getchar(), c != '\n' && c != EOF; )
;
}
printf( "Input was successfully converted to the number %d.", i );
}
This is the output of the program:
Please enter a number: sjdfk
Input was invalid, please try again!
Please enter a number: erlh89
Input was invalid, please try again!
Please enter a number: 34
Input was successfully converted to the number 34.
However, this code has one problem: It will accept input such as "6sdfj23jlj" as valid input for the number 6:
Please enter a number: 6sdfj23jlj
Input was successfully converted to the number 6.
You would probably want to reject the input instead, in this case.
The function scanf will do this, because it is not line-based; it only processes as much input as it can to match the %d conversion format specifier.
One thing you could do to detect such invalid input would be to look at the remainder of the line, and verify that it is empty, apart from the newline character:
#include <stdio.h>
#include <stdbool.h>
int main( void )
{
bool successfully_matched = false;
bool found_newline = false;
int i;
//infinite loop, equivalent to while(1)
//repeat the loop until the input is valid
for (;;)
{
//prompt user for input
printf( "Please enter a number: " );
//attempt to read and convert user input
if ( scanf( "%d", &i ) == 1 )
successfully_matched = true;
//verify that remainder of line is empty
if ( getchar() == '\n' )
found_newline = true;
//break loop if everything was ok
if ( successfully_matched && found_newline )
break;
//print error message
printf( "Input was invalid, please try again!\n" );
//discard remainder of line, if necessary
if ( !found_newline )
{
for ( int c; c = getchar(), c != '\n' && c != EOF; )
;
}
}
printf( "Input was successfully converted to the number %d.", i );
}
Now, the program correctly rejects 6sdfj23jlj as invalid input:
Please enter a number: 6sdfj23jlj
Input was invalid, please try again!
Please enter a number:
However, this code will reject input such as 21 (note the space character after the number). I'm not sure if you want to reject input simply because of a trailing space. If you want to allow spaces and other whitespace characters, then this is possible too, but would require a bit more coding.
In your question, you stated that even if the input is a valid number, you want to additionally check whether the number is in a certain range. This can be accomplished too. However, since the code is starting to get complicated, it seems better to create a new function get_int_from_user to actually get a number from the user. After calling that function, we can then perform the range check, and if the number is not in the desired range, we print an error message and then call the function again.
#include <stdio.h>
#include <stdbool.h>
int get_int_from_user( const char *prompt )
{
bool successfully_matched = false;
bool found_newline = false;
int i;
//infinite loop, equivalent to while(1)
//repeat the loop until the input is valid
for (;;)
{
//prompt user for input
printf( "%s", prompt );
//attempt to read and convert user input
if ( scanf( "%d", &i ) == 1 )
successfully_matched = true;
//verify that remainder of line is empty
if ( getchar() == '\n' )
found_newline = true;
//break loop if everything was ok
if ( successfully_matched && found_newline )
break;
//print error message
printf( "Input was invalid, please try again!\n" );
//discard remainder of line, if necessary
if ( !found_newline )
{
for ( int c; c = getchar(), c != '\n' && c != EOF; )
;
}
}
return i;
}
int main( void )
{
int i;
//repeat until input is in the desired range
for (;;)
{
//read number from user
i = get_int_from_user( "Please enter a number: " );
//perform range check on number
if ( 1 <= i && i <= 30 )
{
//input is in the desired range
break;
}
//print error message
printf( "Number is not in the desired range, please try again!\n" );
}
printf( "The number %d is in the desired range.", i );
}
This program has the following output:
Please enter a number: 342
Number is not in the desired range, please try again!
Please enter a number: 27
The number 27 is in the desired range.
However, I generally do not recommend using scanf for line-based user input. As previously stated, the function scanf does not read one line of input at a time. This means that is can leave leftovers of the line on the input stream, which can be confusing to the programmer, and can lead to bugs, such as this one. See this question for more information on the disadvantages of using scanf.
For line-based user input, it is generally better to use the function fgets. Here is a very robust implementation of the function get_int_from_user which I copied from this previous answer of mine to another question. This function uses fgets and strtol instead of scanf, and performs extensive input validation and error checking:
#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 "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;
}
}
If you want to exclude characters from being in your code you can use something like this:
unsigned long long answer1,answer2,answer3,c;
scanf("%*[^0123456789]%llu%*[^0123456789]%llu%*[^0123456789]%llu",&answer1,&answer2,&answer3);
printf("%lld %lld %lld",answer1,answer2,answer3);
return 0;
and if you want to print characters you shouldn't scan characters like this: scanf("%d"&a) instead you scan them with this: scanf("%c",&a) and the same point stands in print. but with this you can only scan one character at a time and if you want to scan more than that use more %c in the scanf and printf.
When I enter a letter the loop runs infinitely. Does a letter store as a zero when it is input as an int? How can I reject a non digit answer, just I have rejected an answer outside the range?
int main(int argc, const char * argv[]) {
// insert code here...
int categoryToScore;
int categoryScores = 6;
printf("Enter category to save score: ");
scanf("%d", &categoryToScore);
while (categoryToScore >= categoryScores || categoryToScore <= 0) {
printf("Error: invalid command. Enter 1-5 to save to an unused category\n");
printf("Enter category to save score: ");
scanf("%d", &categoryToScore);
}
return 0;
}
Just for background
I want to:
print a request an input that is between 1 and an upper bound
scanf for the input
check if the input is of a correct type and within the correct range
if it isn't then print an error message and go back to 1.
if it is then proceed
You are asking scanf to read a number from standard input. Scanf finds a non-digit character in the standard input and does not remove it from the standard input. Scanf fails and returns 0 (the number of fields successfully processed).
The next time you call scanf, it finds the same character at the start of standard input. So the process repeats indefinitely.
One solution is to read stdin one character at a time.
Another solution is to read (and discard) the one character from stdin before calling scanf again.
int main(int argc, const char * argv[]) {
// insert code here...
int categoryToScore;
int categoryScores = 6;
int scantRetVal;
printf("Enter category to save score: ");
scantRetVal = scanf("%d", &categoryToScore);
if (scantRetVal != 1) {
getchar(); // read and discard one character from stdin
categoryToScore = 0;
}
while (categoryToScore >= categoryScores || categoryToScore <= 0) {
printf("Error: invalid command. Enter 1-5 to save to an unused category\n");
printf("Enter category to save score: ");
scantRetVal = scanf("%d", &categoryToScore);
if (scantRetVal != 1) {
getchar(); // read and discard one character from stdin
categoryToScore = 0;
}
}
return 0;
}
Rather than fix this particular program I will show how to solve ANY similar problem using a concept called an "exit condition".
The idea of an exit condition is that you have an infinite loop and it has various exit conditions. Often there are two exit conditions: one for success and one for an error.
while( true ){ /* infinite loop */
char c = ... /* get the character you want */
if( c < '0' || c > '9' ){
printf( "invalid character, not a digit\n" );
continue; // get another character
}
... /* do whatever you with valid data */
if( c == '3' ) break; /* your exit condition, whatever it is */
if( c == '7' ) exit(0); /* exit the whole program */
}
Note: If you are accepting free form input (numbers and strings), scanf is probably not a good idea. scanf accepts very specific, well-formatted input. So if you ask for a %d, then there better be a %d (decimal number) in the input or you will have problems.
For example, if you accept numbers and strings, you should take everything as strings using fgets or something like that.
Here is a complete program that does what you want:
#include <stdio.h>
#include <stdbool.h>
int main(int argc, const char * argv[]) {
int iMaxScore = 6;
int charInput = 0;
int iInputValue = 0;
while( true ){
printf("Enter category to save score: ");
GetInput:
charInput = getchar();
if( charInput == 10 || charInput == 13 ) goto GetInput; /* ignore enter key */
if( charInput == 'q' ) break;
if( charInput < '0' || charInput > '9' ){
printf( "invalid entry, not a digit %d\n", charInput );
break;
}
iInputValue = charInput - '0';
if( iInputValue > iMaxScore ){
printf( "Error, input value exceeds maximum category %d\n", iMaxScore );
continue; /* try again */
}
printf( "you entered category %d\n", iInputValue );
/* continue ... */
}
return 0;
}