C Program Looping Incorrectly - c

I'm just a beginner and I'm trying to use whatever I know to make a simple program that:
Asks the user to input the letter 'S' or 's'. The program loops if 's' is not input. If the user does input 's', the program then
Asks the user to input a number, 1 or 2. The program loops if the incorrect number is input.
The problem I'm having is that after 's' is successfully input and the user is asked to enter a number, if an incorrect number is input (not 1 or 2) the program asks the user to input a letter again from the beginning which is incorrect. The program loops from the very beginning and doesn't work anymore. Can anyone help me fix this please?
#include <stdio.h>
#include <ctype.h>
int function(int num);
int main()
{
char input,ch,temp,c[64],exit;
int i,invalid,num,index,flag,day;
invalid = 0;
num = 0;
size_t length = 0;
index = 0;
flag = 0;
do
{
puts("Enter the letter S to start the program:");
scanf("%c", &input);
while( input!='\n' && (ch=getchar())!='\n' && ch!= EOF);
{
if(isalpha(input)==0)
{
printf("Invalid input. Please input something.\n");
continue;
}
if(input == 'S' || input == 's')
{
printf("\nProgram start.");
while( sscanf(c, "%d", &num) != 1)
{
length = 0;
flag = 0;
num = 0;
printf("\nEnter 1 for Module A. Enter 2 for Module B. Enter here: ");
fgets(c, 63, stdin);
length = strlen(c);
for(index = 0; index < length; ++index)
{
if(c[index] < '0' || c[index] > '9')
{
flag = 1;
break;
}
}
if( flag)
{
printf("\nInvalid character\n");
continue;
}
if( sscanf(c, "%d", &num) != 1)
{
printf("\nNo input detected.");
continue;
}
if(num == 1)
{
printf("\nModule A Selected.\n");
return(0);
}
if(num == 2)
{
printf("\nModule B Selected.\n");
return(0);
}
}
}
else
{
printf("\nInvalid input.");
continue;
}
}
}
while(1);
}

Make the scanf into like this.
scanf(" %c",&input);
Then While getting the input from the user using fgets It will place the new line character into that buffer. So this will lead to fails this condition.
if(c[index] < '0' || c[index] > '9')
{
flag = 1;
break;
}
So make the this condition into like this.
length=strlen(c)-1;// to skip the new line character
Or else to like this.
length=strlen(c);
if ( c[length] == '\n' )
c[length]='\0';
Output After placing this,
Enter the letter S to start the program:
S
Program start.
Enter 1 for Module A. Enter 2 for Module B. Enter here: 1
Module A Selected.
Make this in you code.
if(num == 1)
{
printf("\nModule A Selected.\n");
return(0);
}
else if(num == 2)
{
printf("\nModule B Selected.\n");
return(0);
}
else
{
printf("\nInvalid option\n");
c[0]='\0'; // It is for satisfy the second while loop condition.
continue;
}

Note that the loop:
while( input!='\n' && (ch=getchar())!='\n' && ch!= EOF);
is limited to the one line by the semicolon at the end. The following code is not the body of the loop, despite indentation trying to pretend that it is.
Also note that getchar() returns an int, not a char; you cannot reliably assign the result to a char and then test it for EOF. Depending on the platform, you will either never detect EOF at all or you will misdetect EOF when some other character (often ÿ, y-umlaut, U+00FF, LATIN SMALL LETTER Y WITH DIAERESIS) is typed. You must use int ch;.

Here. I fixed the problem using the following code. This way the code does the following:
Scans letters 'S' or 's'. Keeps looping if these are not entered.
Scans either number 1 or 2. Keeps looping until either number is entered and then exits.
The program does not loop from the very beginning (by outputting "Enter 'S' to start program), if any number other than 1 or 2 in entered in part 2 of the program. This was the problem originally.
The following is the correct code:
#include <stdio.h>
#include <ctype.h>
int function();
char input,temp,c[64],ch,exit;
int i,invalid,num,index,flag,start;
start = 0;
invalid = 0;
num = 0;
size_t length = 0;
index = 0;
flag = 0;
int main()
{
do
{
puts("Enter the letter S to start the program: ");
scanf("%c", &input);
while( input!='\n' && (ch=getchar())!='\n' && ch!= EOF);
{
if(isalpha(input)==0)
{
printf("Invalid input. Please input something.\n");
continue;
}
if(input == 'S' || input == 's')
{
printf("\nProgram start.");
start = 1;
if(start == 1)
{
function();
return(0);
}
}
else
{
printf("\nInvalid input.");
continue;
}
}
}
while(1);
}
int function()
{
while( sscanf(c, "%d", &num) != 1)
{
length = 0;
flag = 0;
num = 0;
printf("\nEnter 1 for Module A. Enter 2 for Module B. Enter here: ");
fgets(c, 63, stdin);
length = strlen(c);
length --;
for(index = 0; index < length; ++index)
{
if(c[index] < '0' || c[index] > '9')
{
flag = 1;
break;
}
}
if( flag)
{
printf("\nInvalid character\n");
continue;
}
if( sscanf(c, "%d", &num) != 1)
{
printf("\nNo input detected.");
continue;
}
if(num == 1)
{
printf("\nModule A Selected.\n");
return(0);
}
else if(num == 2)
{
printf("\nModule B Selected.\n");
return(0);
}
else
{
printf("\nInvalid option\n");
c[0]='\0'; // It is for satisfy the second while loop condition.
continue;
}
}
}

Related

Input Validation in C When Calculating Grades

I have a problem that needs to calculate the grade needed on a final exam in order to get the letter grade that they desire. I have the code correct for the calculations but I need to account for all invalid user input. I have accounted for negative grades and letter grades that don't exist but I can't figure out how to make sure the percentages they are inputting don't contain letters or other characters. For example if I ask for their current grade in the class they cannot input something like 95.6asi!. This is the code I have right now but there are many errors.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
void calcGradeNeededOnFinal() {
double percentWanted = 0.0;
double currentGrade, finalWeight;
char gradeWanted;
printf("Enter the grade you want in the class: ");
scanf("%c", &gradeWanted);
if (gradeWanted == 'A' || gradeWanted == 'a'){
percentWanted = 90.0;
}
else if (gradeWanted == 'B' || gradeWanted == 'b'){
percentWanted = 80.0;
}
else if (gradeWanted == 'C' || gradeWanted == 'c'){
percentWanted = 70.0;
}
else if (gradeWanted == 'D' || gradeWanted == 'd'){
percentWanted = 60.0;
}
else if (gradeWanted == 'F' || gradeWanted == 'f'){
percentWanted = 0.0;
}
else {
printf("Unknown Grade Received: %c. Ending Program.\n", gradeWanted);
exit(0);
}
printf("Enter your current percent in the class: ");
scanf("%lf", &currentGrade);
if(currentGrade < 0) {
printf("The number you last entered should have been positive. Ending program.\n");
exit(0);
}
char gradeString = (char)currentGrade;
for(int i=0; i < strlen(gradeString); ++i) {
if(!(isdigit(gradeString[i])) && (strcmp(gradeString[i], '.') != 0))) {
printf("Invalid formatting. Ending program.\n");
exit(0);
}
}
printf("Enter the weight of the final: ");
scanf("%lf", &finalWeight);
if(finalWeight < 0) {
printf("The number you last entered should have been positive. Ending program.\n");
exit(0);
}
char weightString = (char)finalWeight;
for(int i=0; i < strlen(weightString); ++i) {
if(!(isdigit(weightString[i])) && (strcmp(weightString[i], '.') != 0))) {
printf("Invalid formatting. Ending program.\n");
exit(0);
}
}
// this calculates the grade need on the final test to get the desired grade of the user
double gradeNeededOnFinal = (percentWanted - (currentGrade * (1.0 - finalWeight/100.0))) / (finalWeight/100.0);
printf("You need to get at least %.2lf%% on the final to get a %c in the class.\n", gradeNeededOnFinal, gradeWanted);
}
int main() {
calcGradeNeededOnFinal();
return 0;
}
For example if I ask for their current grade in the class they cannot input something like 95.6asi!.
You have declared grade as char so there is no possibility that it can store 95.6asi!
char gradeWanted;
What you need is : isalpha or isupper and islower function(s) from ctype.h
isalpha() checks for an alphabetic character; in the standard "C" locale, it is equivalent to (isupper(c) || islower(c)). In some locales, there may be additional characters for which isalpha() is true-letters which are neither upper case nor lower case.
Just check if your gradeWanted is an alphabet or not.
Using
if (isalpha( gradeWanted ))
or
if (isupper( gradeWanted ) || islower( gradeWanted ))
is enough.
All ctype functions takes an int and returns an int, you can declare char gradeWanted; as int gradeWanted;
you could read the characters entered and, using the ascii encoding, check (perhaps with a switch) which character is inserted and manage it.
This way you have full control of the entries.
you can get char to ascii conversion with:
int a_as_int = (int)'a';

How do you print different things depending the user input?

First, I apologize if the question doesn't make sense as my English isn't that good...
My question is, how do we print out different things depending on the user input?
What I'm trying to do is: when user inputs integer, the program prints out the inputted number. When the user inputs something that's not integer (like symbols and characters), the program prints out "not integer".
my current idea (pseudo-code) is as follows:
`int main(){
int value;
printf("Enter numbers");
scanf("%d", &value);
if(value is integer){
printf("%d", value);
} else {
printf("not integer");
}
return 0;
}`
what gets me is the scanf; by using %d, I'm assuming that the user will input an integer values, but the user can input values that are not integers so I can't make a comparison using the if statement if( value is integer). How can I make a comparison that will determine whether the inputted value is integer or not?
I don't know if this is a good thing or not.
You can use ASCII to check if the input type is an integer or not
(between 48 - 57 in ASCII)
it will be like this
char value;
int flag = 0; //to check true or false (0 means false, and 1 means true)
printf("Enter numbers");
scanf("%c", &value);
for(int i = 48; i <= 57; i++){
if(value == i){
flag = 1;
break;
}
}
if(flag == 1){
printf("%c", value);
} else {
printf("not integer");
}
How do you print different things depending the user input?
Step 1: Read the line of user input
char buf[100];
if (fget(buf, sizeof buf, stdin)) {
// something was entered
Step 2: test the string
char *end;
long value = strtol(buf, *end);
// If the end is the same as the beginning, no conversion occurred.
if (end == buf) {
puts("not integer");
}
printf("%ld\n", value);
}
}
Additional code could look for input that occurred after the integer. Also code could test for a large number that overflowed the long range.
The code is as follows. It caters for different situations like inputting negative numbers and decimal numbers:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main() {
char input[20];
int wrongFlag = 0;
scanf("%s", input);
if (input[0] == '0' && strlen(input) > 1) {
wrongFlag = 1;
//for number starts with 0, and string length>1 eg: 010
}
for (int i = 0; i < strlen(input); i++) {
if (i == 0 && (input[i] == '-' && strlen(input) > 2 && input[i + 1] == '0')) {
//check first round only: negative number with length >2 and starts with 0 eg: -010.
wrongFlag = 1;
continue;
}
if (i != 0 && !isdigit(input[i])) {
//check following rounds, check if it is not digit
wrongFlag = 1;
break;
}
}
if (wrongFlag) {
printf("Not integer");
}
else {
printf("integer");
}
return 0;
}
Try this it works for me.
#include<stdio.h>
#include<string.h>
int main()
{
int i;
char value[50];
int len;
printf("Enter maximum 50 digits\n");
/* enter the values you wanted*/
printf("Enter the value: ");
gets(value);
len = strlen(value);
/*it will iterate upto the end of the user input*/
for(i=0;i<len;i++)
{
if(48<value[i] && value[i]<=57)
{
if(i==(len-1))
printf("It's an integer");
}
else{
printf(" Not an integer");
break;
}
}
return 0;
}

How to restrict to a one letter input?

I realised that if the input is a word starting with 'y' or 'n', it will escape the loop. How can I restrict the loop such that it will continue looping unless the input is a single character?
do
{
printf("Do you want to try again? (Y/N): ");
fflush(stdin);
scanf("%c", &repeat);
repeat = toupper(repeat);
if (repeat != 'Y' && repeat != 'N')
printf("Invalid answer. Please enter 'Y' or 'N'.\n\n");
} while (repeat != 'N' && repeat != 'Y');
like this:
#include <stdio.h>
#include <ctype.h>
int main(void){
char repeat[3] = {0};//3 : one character + one character + NUL
do{
printf("Do you want to try again? (Y/N): ");fflush(stdout);
if(EOF==scanf("%2s", repeat)){ *repeat = 'N'; break; }
*repeat = toupper(*repeat);
if (repeat[1] || *repeat != 'Y' && *repeat != 'N'){//repeat[1] != '\0'..
printf("Invalid answer. Please enter 'Y' or 'N'.\n\n");
scanf("%*[^\n]");scanf("%*c");//clear upto newline
*repeat = 0;
}
} while (*repeat != 'N' && *repeat != 'Y');
puts("Bye!");//try agein or see ya, bye
return 0;
}
First fflush(stdin); does not make sense except in Microsoft's world.
Then, the scanf family function returns a value which is the number of input token successfully decoded and that return value should always be controlled. And %c should be used with caution because it can return a blank character (space or newline) remaining in buffer while %s only return printable characters. With those remarks you code could become:
repeat = '\0';
do
{
char dummy[2], inp[2];
printf("Do you want to try again? (Y/N): ");
// fflush(stdin);
if (1 == scanf("%1s%1s", inp,dummy) repeat = toupper(inp[0]);
if (repeat != 'Y' && repeat != 'N')
printf("Invalid answer. Please enter 'Y' or 'N'.\n\n");
} while (repeat != 'N' && repeat != 'Y');
Alternatively to using scanf() one can use fgets() to read a line and then do the parsing one self:
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char repeat = '\0';
do
{
int input_valid = 0; /* Be pessimistic. */
char line[3] = {0};
puts("Do you want to try again? (Y/N):");
do /* On-time loop, to break out on parsing error. */
{
if (NULL == fgets(line, sizeof line, stdin))
{
break; /* Either fgets() failed or EOF was read. Start over ... */
}
if (line[1] != '\0' && line[1] != '\n')
{
break; /* There was more then one character read. Start over ... */
}
line[0] = toupper(line[0]);
if (line[0] != 'Y' && line[0] != 'N')
{
break; /* Something else but Y or N was read. Start over ... */
}
input_valid = 1;
} while (0);
if (input_valid == 0)
{
int c;
do /* Flush rest of input. if any. */
{
c = getc(stdin);
} while (EOF != c && '\n' != c);
fprintf(stderr, "Invalid answer. Please enter 'Y' or 'N'.\n\n");
}
else
{
repeat = line[0];
}
} while ('\0' == repeat);
printf("The user entered: '%c'\n", repeat); /* Will only print either Y or N. */
return EXIT_SUCCESS;
}

Logic in detecting int in C

I would appreciate some help with this. I'm trying to create this simple program that repeatedly loops asking for the user to enter in an int. If he enters an int, it exits but if he enters something else or bigger than int (ex.4gs4r33) it will loop again asking to enter an int. This is what I have tried, and it's not working. It says it's an int even if it's not.
#include<stdio.h>
unsigned int findInt();
int main() {
printf("Please input an int.\n");
findInt();
}
unsigned int findInt() {
char input;
long num = 0;
int b = 0;
do {
scanf("%c", &input);
if (isdigit(input)){
num = num*10+input+'0';
b = 1;
}
else if (input == '\n')
b = 1;
else
b = 0;
} while(input != '\n');
if (b == 1)
printf("Great!\n");
else{
printf("Not an int \n");
findInt();
}
return 0;
}
Two possible approaches. One would be to modify your code:
b = 1; // start off with good intentions…
do {
scanf("%c", &input);
if (isdigit(input)){
num = num*10+input -'0'; // *** SUBTRACT '0', don't add it!
}
else if (input != '\n') {
b = 0;
break; // *** break when you find non-digit
}
} while (input != '\n');
Two changes: getting the math right as you compute the integer, and fixing the logic (so you break out of your loop when you find a non digit character)
Second approach:
char buf[100];
char intAsString[100];
fgets(buf, 100, stdin);
sscanf(buf, "%d", num);
sprintf(intAsString, "%d\n", num);;
if(strcmp(buf, intAsString) == 0 ) {
printf("yay - you entered an integer!\n");
}
I'm sure you can figure out how that works.
update a complete code snippet that solves the issue of "loop logic" as well: you call the findInt function once from the top level, and it keeps going until you get the int. Note - in order for this to work properly, I read the entire input at once (rather than one at a time), then pick off the characters one by one using sscanf (and updating the pointer manually). It has a number of advantages - not least of which is that you start with a fresh input every time you call findInt, instead of having the rest of the input buffer that still needs reading (and which was giving rise to "no,no,no,great!" - as you would keep reading the bad input until you got to the newline, and accept that...)
#include<stdio.h>
#include <ctype.h>
unsigned int findInt();
int main() {
findInt();
}
unsigned int findInt() {
char input;
char buf[100];
char *temp;
long num = 0;
int b = 0;
printf("please enter an int:\n");
fgets(buf, 100, stdin);
temp = buf;
do {
sscanf(temp++, "%c", &input);
if (isdigit(input)){
num = num*10+input-'0';
b = 1;
}
else if (input == '\n')
{
b = 1;
break;
}
else {
b = 0;
break;
}
} while(input != '\n');
if (b == 1)
printf("Great! %d is an integer!\n", num);
else{
printf("Not an int \n");
findInt();
}
return 0;
}
In the else branch - i.e. not a digit or a newline - you set b to 0. Now if a digit DOES follow you reset that to 1.
You'll probably want to break or somehow record the permanent failure instead of just continuing.
#include <stdlib.h>
#include <limits.h>
#include <stdbool.h>
void findInt(){
int x;
bool ok;
do{
char buff[32], *endp;
long long num;
ok = true;//start true(OK)
printf("Enter a number: ");
fgets(buff, sizeof(buff), stdin);
//strtoll : C99
x=(int)(num=strtoll(buff, &endp, 0));//0: number literal of C. 10 : decimal number.
if(*endp != '\n'){
if(*endp == '\0'){
printf("Too large!\n");//buffer over
while('\n'!=getchar());
} else {
printf("Character that can't be interpreted as a number has been entered.\n");
printf("%s", buff);
printf("%*s^\n", (int)(endp - buff), "");
}
ok = false;
} else if(num > INT_MAX){
printf("Too large!\n");
ok = false;
} else if(num < INT_MIN){
printf("Too small!\n");
ok = false;
}
}while(!ok);
}
,

How to expect different data types in scanf()?

I'm developing a chess game in C just for practicing. At the beginning of the game, the user can type 4 things:
ROW<whitespace>COL (i.e. 2 2)
'h' for help
'q' to quit
How can I use a scanf to expect 2 integers or 1 char?
Seems like it would be most sensible to read a whole line, and then decide what it contains. This will not include using scanf, since it would consume the contents stdin stream.
Try something like this :
char input[128] = {0};
unsigned int row, col;
if(fgets(input, sizeof(input), stdin))
{
if(input[0] == 'h' && input[1] == '\n' && input[2] == '\0')
{
// help
}
else if(input[0] == 'q' && input[1] == '\n' && input[2] == '\0')
{
// quit
}
else if((sscanf(input, "%u %u\n", &row, &col) == 2))
{
// row and column
}
else
{
// error
}
}
It's better to avoid using scanf at all. It usually causes more trouble than what it solves.
One possible solution is to use fgets to get the whole line and then use strcmp to see if the user typed 'h' or 'q'. If not, use sscanf to get row and column.
This one is just using scanf
#include <stdio.h>
int main()
{
char c;
int row, col;
scanf("%c", &c);
if (c == 'h')
return 0;
if (c == 'q')
return 0;
if (isdigit(c)) {
row = c - '0';
scanf("%d", &col);
printf("row %d col %d", row, col);
}
return 0;
}
int row, col;
char cmd;
char *s = NULL;
int slen = 0;
if (getline(&s, &slen, stdin) != -1) {
if (sscanf(s, "%d %d", &row, &col) == 2) {
free(s);
// use row and col
}
else if (sscanf(s, "%c", &cmd) == 1) {
free(s);
// use cmd
}
else {
// error
}
}
P.S.: those who did not read and understand my answer carefully, please respect yourself, DO NOT VOTE-DOWN AT WILL!
Beside "get the whole line and then use sscanf", read char by char until '\n' was entered is also a better way. If the program encountered 'h' or 'q', it could do the relevant action immediately, meanwhile you cloud also provide a realtime analysis for the input stream.
example:
#define ROW_IDX 0
#define COL_IDX 1
int c;
int buffer[2] = {0,0};
int buff_pos;
while( (c = getchar())) {
if (c == '\n') {
//a line was finished
/*
row = buffer[ROW_IDX];
col = buffer[COL_IDX];
*/
buff_pos = 0;
memset(buffer , 0 , sizeof(buffer));//clear the buffer after do sth...
} else if (c == 'h') {
//help
} else if (c == 'q') {
//quit
} else {
//assume the input is valid number, u'd better verify whether input is between '0' and '9'
if (c == ' ') {
//meet whitespace, switch the buffer from 'row' to 'col'
++buff_pos;
} else {
buffer[buff_pos%2] *= 10;
buffer[buff_pos%2] += c - '0';
}
}
}

Resources