scan arbitrary number of chars in c - c

I want to read an unknown number of characters, which are not greater than 10.
char word[10];
for( i=0;i<10;i++){
if( !scanf("%c",&word[i])){ //terminate with 0
getchar();
break;
}
}
The problem is that number is also an character, so the if statement won't be executed. Is there any other solution to terminate the input of characters for example with 0.

Suggest:
char word[10];
if( scanf("%9s",word) != 1 )
{
fprintf( stderr, "scanf for (max 9 char word) failed\n" );
exit( EXIT_FAILURE );
}
using %9s because the %s input format conversion specifier always appends a NUL byte to the input.
If the input is less than 9 characters, that is properly handled.
If the input is greater than 9 characters, the 9 modifier will stop the input, so the input buffer is not overflowed. Such an overflow would result in undefined behavior.

you can check the character that you just read (at word[i]) and if not valid (e.g not alpabetical) than break.

You can use a do..while loop. Something like (pseudo-code)
int keep_looping = 1;
int counter = 0;
do {
ret = scanf(" %c",&word[counter]);
if (!ret) continue; //can use more cleanup and error check
if (word[counter] == '0') keep_looping =0;
counter++;
}
while (keep_looping && counter < 10)

Related

c programming: Error handling. How do I stop characters from being entered into program

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.

converting an entered 5 digit number into words

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;
}

Wanted to check if the value entered is a number or else. "isdigit() is not working here as expected

I am stuck with a problem here in C. I am posting the question and the code I have written below. Here I have to enter 10 numbers in an array and then I need to check how many times a number appeared. But to verify that I have entered a number and not anything else, I have used "isdigit()" function. But this is of no use. Can anyone help me to solve it.
/*
(a) Ten numbers are entered from the keyboard into an array. The number to be searched is entered through the
keyboard by the user. Write a program to find if the number to be searched is present in the array and if it is present, display
the number of times it appears in the array.
*/
#include<stdio.h>
#include<ctype.h>
main()
{
int num[10];
int i, j, cnt=0;
char rept;
printf("Enter 10 numbers: \n\n");
for(i=0; i<=9; i++)
{
printf("Number %d = ", i+1);
scanf("%d", &num[i]);
fflush(stdin);
if ( !isdigit(num[i]) )
{
printf("OK\n");
}
else
{
printf("Invalid number. Enter again.\n");
i=i-1;
}
}
do
{
printf("\nEnter the number to be searched in array: ");
scanf(" %d", &j);
for (i=0 ; i<=24; i++)
{
if(num[i]==j)
cnt++;
}
if(cnt>0)
printf("\nNumber %d is present at %d places", j, cnt);
else
printf("\nNumber not present.");
printf("\n\nDo you want to search another number. Press Y to repeat. Any other key to exit");
fflush(stdin);
scanf("%c", &rept);
}while (rept=='y'||rept=='Y');
getch();
}
No you can't do that. isdigit() is supposed to work with characters and you passed a multigit integer variable.
What you can do is simply like this
if( scanf("%d",&a[i])== 1){
// you can be sure number is entered
}
And fflush(stdin) is undefined behavior.
So the use of scanf will be more prominent if you would do this
int clearstdin(){
int c;
while ((c = getchar()) != '\n' && c != EOF);
return (c == EOF);
}
In main()
int earlyend = 0;
for(size_t i=0; i<SIZE; i++){
...
...
int ret = scanf("%d",&a[i]);
while( ret == 0){
if( clearstdin() ){ /* EOF found */earlyend = 1; break; }
fprintf(stderr,"%s\n","Entered something wrong");
ret = scanf("%d",&a[i]);
}
if( earlyend ){ /*EOF found*/ }
if( ret == EOF) { /* Error occured */}
...
}
The %d conversion specifier will cause scanf to skip over any leading whitespace, then read a sequence of decimal digits, stopping at the first non-digit character. If there are no digit characters in the input (for example, you enter something like ”abc”), then nothing is read from the input stream, a[i] is not updated, and scanf will return 0 to indicate a matching failure.
So, you can do a test like
if ( scanf( “%d”, &a[i] ) == 1 )
{
// user entered valid input
}
But...
This doesn’t fully protect you from bad input. Suppose you enter something like ”123abc” - scanf will read, convert, and assign 123 and return a 1 indicating success, leaving ”abc” in the input stream to potentially foul up the next read.
Ideally, you’d like to reject the whole thing outright. Personally, I do this as follows:
char inbuf[SOME_SIZE]; // buffer to store input
if ( fgets( inbuf, sizeof inbuf, stdin ) ) // read input as text
{
char *chk; // use strtol to convert text to integer
int temp = (int) strtol( inbuf, &chk, 10 ); // first non-digit character written to chk
if ( isspace( *chk ) || *chk == 0 ) // if chk is whitespace or 0, input is valid
{
a[i] = temp;
}
else
{
// bad input
}
}
This still isn’t a 100% solution - it doesn’t make sure the user didn’t enter more characters than the buffer can hold, but it’s a step in the right direction.
Input validation in C is, frankly, a pain in the ass.

Why does the printf function print the value input even when the input value in scanf is not equal to 1

I do not understand why the condition in does not reflect the results. I input values that were not equal to 1 as specified by the condition and it still printed. Can someone please explain to me why this is the case.
#include<stdio.h>
int main() {
int n;
while ( scanf( "%d", &n) == 1 )
printf("%d\n",n);
return 0;
}
scanf returns the number of inputs read and assigned, not the value of the input itself. In this particular case you are only expecting a single input, so scanf will return 1 on success, 0 on a matching failure (i.e., the input doesn’t start with a decimal digit), or EOF if it sees end-of-file or an error.
If you want to test against the value of the input, you’d do something like
while( scanf( “%d”, &n ) == 1 && n == EXPECTED_VALUE )
{
printf( “%d”, n );
}
Edit
Actually, a better way to do that would be something like this:
int n;
int itemsRead;
/**
* Read from standard input until we see an end-of-file
* indication.
*/
while( (itemsRead = scanf( "%d", &n )) != EOF )
{
/**
* If itemsRead is 0, that means we had a matching failure;
* the first non-whitespace character in the input stream was
* not a decimal digit character. scanf() doesn't remove non-
* matching characters from the input stream, so we use getchar()
* to read and discard characters until we see the next whitespace
* character.
*/
if ( itemsRead == 0 )
{
printf( "Bad input - clearing out bad characters...\n" );
while ( !isspace( getchar() ) )
// empty loop
;
}
else if ( n == EXPECTED_VALUE )
{
printf( "%d\n", n );
}
}
if ( feof( stdin ) )
{
printf( "Saw EOF on standard input\n" );
}
else
{
printf( "Error while reading from standard input\n" );
}
The problem is that you are not comparing the value of n which is the input read, but the value returned by the scanf function which is the number of inputs you have, in your case is always 1.
More details: Value returned by scanf function in c
This code should works in your case:
#include<stdio.h>
int main() {
int n;
scanf("%d", &n);
while(n == 1){
printf("%d\n",n);
scanf("%d", &n);
}
return 0;
}
I think that you are not comparing correctly n variable with 1.
So if I dont be wrong. Try to compare n with 1.
int main() {
int n;
while ( scanf( "%d", &n) == 1){
if(n!=1){
break;
}
printf("%d\n",n);
}
return 0;
}
This can be a sloppy answer, but it is an example.

How to reject letter when requesting int in C

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;
}

Resources