Input Validation in C When Calculating Grades - c

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

Related

Change(?) user input character into another character c language

Writing a program for school to cause a user inputted character to identify a gas cylinder. Here is what i have written so far, but all inputs display ammonia.
#include <stdio.h>
int main()
{
char n;
printf("Please enter the first letter of color of gas cylinder\n");
scanf("%c", &n);
if(n = 'o' ){
printf("Ammonia\n");
}
else if(n = 'b'){
printf("Carbon Monoxide\n");
}
else if(n = 'y'){
printf("Hydrogen\n");
}
else if(n = 'g'){
printf("Oxygen\n");
}
else{
printf("Contents Unknown\n");
}
return 0;
}
You want to use ==—the equality operator—inside your if statements.
= is the assignment operator—it will set n to be 'o', so your first if statement always returns true ('o' is non-zero).

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

C : how can I type a name and end it with '.' (dot)

My assignment is to write the last name of a doctor with only small letters and end it with a dot ('.') that the user will print with the output. The expended assignment is to build a system for medical appointments:
#include<stdio.h>
#include<stdlib.h>
int main(){
fflush(stdin);
int count = 0, flag = 0;
char Nameplate; //last name of the doctor
printf("Please enter the last name of your doctor(please type with only small letters):\n");
Nameplate = getchar();
do{
Nameplate = getchar();
} while (Nameplate >= 'a' && Nameplate <= 'z' && Nameplate == '.');
if (!(Nameplate >= 'a' && Nameplate <= 'z' && Nameplate == '.')){
flag = 1;
}
else if (Nameplate == '\n'){
flag = 0;
count++;
}
if (flag == 1){
printf("Invalid input,");
fflush(stdin);
main();
}
else if (flag == 0){
printf("\n Your appointment has been successfully canceled.\n\n");
}
return 0;
}
Now, this code is not working. It is working if I do not use the dot, but when I enter the dot, the problems start.
Try
while ((Nameplate >= 'a' && Nameplate <= 'z') || (Nameplate == '.'))
instead of
while (Nameplate >= 'a' && Nameplate <= 'z' && Nameplate == '.')
Even with a change in the test for lower case letters and period, you still have a potential problem because the user might have a period in the name, but still not have a period at the end.
This code checks for lower case letters only, excluding the last character, and then tests that the last character is a period.
Also there is some control on name length.
int main()
{
int flag, n;
char *Nameplate = malloc(22); //last name of the doctor
fflush(stdin);
while(1) {
printf("Please enter the last name of your doctor(please type with only small letters):\n");
printf("End name with a period/full stop\n");
fgets(Nameplate, 20, stdin);
/* test for all valid lower case letters */
flag = 0;
for(n = 0; n < strlen(Nameplate) - 2; n++) {
if (!('a' <= Nameplate[n] && Nameplate[n] <= 'z')) {
/* not a lower case character */
flag = 1;
break;
}
}
/* now test for terminating period */
if(Nameplate[strlen(Nameplate) - 2] != '.') {
/* no period at end of name */
flag = 2;
}
/* handle errors or accept */
if(flag == 1) {
printf("small letters only\n");
} else if (flag == 2) {
printf("remember to end with a period/full stop\n");
} else {
/* name was all lower case with terminating period so exit input */
break;
}
}
printf("\n Your appointment with %s has been successfully canceled.\n\n", Nameplate);
free(Nameplate);
return 0;
}
If I understand you want to enter a doctor's name to set an appointment, only handle lower-case characters, and if the last character is a '.' cancel the appointment, you can rearrange your logic slightly and accomplish that goal while still preserving middle-initial punctuation.
Additionally, any time you are taking input, provide a prompt for your user so they are not sitting there looking at a blinking cursor wondering if the program hung.
When handling upper/lower-case conversion, there is no reason to force the user to enter in only one or the other. You can simply check/convert whatever input they provide in a transparent fashion. The ctype.h header file holds the tolower and toupper character conversions, or you can simply understand that the 6th-bit in 7-bit ASCII is the case-bit and toggle it as needed to accomplish the case-conversion.
This is just one example, there are many ways to put the pieces together:
#include <stdio.h>
#define MAXC 64
int main (void) {
int c = 0, cnx = 0, i = 0;
char name[MAXC] = "";
printf ("\n enter doctor's name (end with '.' to cancel): ");
while (i + 1 < MAXC && (c = getchar()) != '\n' && c != EOF)
if (('a' <= c && c <= 'z') || c == '.' || c == ' ')
name[i++] = c; /* add to name */
else if ('A' <= c && c <= 'Z') /* if upper-case */
name[i++] = c ^ (1u << 5); /* convert to lower */
if (i && name[i-1] == '.') { /* last is '.' */
cnx = 1; /* set cancel flag */
name[--i] = 0; /* overwrite last '.' */
}
else
name[i] = 0; /* nul-terminate name */
if (cnx) /* appointment canceled */
printf ("\n Your appointment with doctor '%s' has been canceled.\n\n",
name);
else /* new appointment */
printf ("\n You have a new appointment with doctor '%s'.\n\n", name);
return 0;
}
Use/Output
$ ./bin/appointment
enter doctor's name (end with '.' to cancel): John J. Marks
You have a new appointment with doctor 'john j. marks'.
$ ./bin/appointment
enter doctor's name (end with '.' to cancel): John J. Marks.
Your appointment with doctor 'john j. marks' has been canceled.

C Program Looping Incorrectly

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

Numeric input validation in C

I am doing a lab for an intro programming class
I have to make sure that an integer is entered. I thought this would do it but when I put in a letter it repeats in an endless loop.
I found this solution in another post
int num;
char term;
if (scanf("%d%c", &num, &term) != 2 || term != '\n')
printf("failure\n");
else
printf("valid integer followed by enter key\n");
But im not sure what I did wrong. Why is it not working in my code?
#include <stdio.h>
int main(void)
{
int oneVar;
char term;
double numOne;
double numTwo;
double sum;
double dif;
double quo;
double mult;
int checker = 1;
do
{
printf("Please choose one of the following:\n""1) Add\n""2) Subtract\n""3) Divide\n""4) Multiply\n""5) Quit\n");
if (scanf("%d%c" , &oneVar ,&term) != 2 || term != '\n')
{
printf ("This is not valid input\n\n");
checker = 1;
}
else if (oneVar == 5)
{
printf("Thank you. Goodbye.\n");
checker = 0;
}
else if (oneVar != 1 && oneVar !=2 && oneVar != 3 && oneVar != 4)
{
printf("This is not a valid input\n\n");
checker = 1;
}
else
{
printf("Please enter the first number:\n");
if (scanf("%lf%c" , &numOne ,&term) != 2 || term != '\n')
{
printf ("This is not valid input\n\n");
checker = 1;
}
printf("Please enter the second number:\n");
if (scanf("%lf%c" , &numTwo ,&term) != 2 || term != '\n')
{
printf ("This is not valid input\n\n");
checker = 1;
}
else if (oneVar == 1)
{
sum = numOne + numTwo;
printf("The sum is: %.2lf\n" ,sum);
checker = 0;
}
else if (oneVar == 2)
{
dif = numOne - numTwo;
printf("The difference is: %.2lf\n" ,dif);
checker = 0;
}
else if (oneVar == 3)
{
quo = numOne / numTwo;
printf("The quotient is: %.2lf\n" ,quo);
checker = 0;
}
else if (oneVar == 4)
{
mult = numOne * numTwo;
printf("The product is: %.2lf\n" ,mult);
checker = 0;
}
else if (oneVar == 5)
{
printf("Thank you. Goodbye.\n");
checker = 0;
}
}
} while (checker == 1);
return(0);
}
My prof posted this Im not sure how it helps but I thought it might help someone
To make sure that a user-input number is an integer you can use the notion of casting. Casting is a way to tell C to treat a variable as if it were a variable of a different type.
so, if I have something like this:
double myDouble;
myDouble = 5.43;
printf ("%d", (int) myDouble);
It will tell C to print myDouble, but to treat it like an integer. Only the 5 will be printed and you won't get any type mismatch errors. You can use casting to check to see if an input number is an integer by comparing the input to the (int) cast of the number. Something like this should work:
if(inputNum == (int) inputNum)
You'll still get 1.0 and 2.0 passing as valid numbers, but that is ok for now.
Why complicate things?
char x = 0;
scanf("%c", &x);
if (x >= 0x41 && x <= 0x7A)
printf("you entered a letter");
In ASCII table, letters have values between 0x41 ("A") and 0x7A ("z").
So, you just need to check the ASCII value of the input. :)
Using the %c to "consume" the end of line is not a good solution. If the user enters say:
123 abc<newline>
num will be 123, but term will be the space character. If you enter a letter rather than a number, the scan will stop without consuming any of the characters, the next input call will return due to the already buffered line, and may still consume nothing. Your program loops continuously because every input statement is failing to consume the newline and returns immediately. The standard input functions wait for a complete line before returning, if the line is not read completely, input functions do not need to wait.
There are a number of solutions, many of which such as the one you used are flawed, the method below, forces the input buffer to be flushed up to and including the newline.
int check = scanf( "%d", &num ) ;
while( getchar() != '\n' )
{
// do nothing
}
if( check != 2 )
printf("failure\n");
else
printf("valid integer followed by enter key\n");
If you use the %c format specifier at the end of the input, then a slightly different flush is necessary since the character input may be a newline:
int check = scanf( "%c", &ch ) ;
while( ch != '\n' && getchar() != '\n' )
{
// do nothing
}

Resources