input validation with getline and (!(cin >> var)) - c++

I'm working on a project for my c++ class, but I'm a little hung up on how to properly validate input to make sure it's correct before proceeding. The following is a function designed to allow user input for a student's name and test scores. My issues involve 'getline' on line 6 and (!(cin >> score[i][j])) on line 11.
For getline, I need a way to check that only alphabetical characters were entered.
For (!(cin >> score...)) If the user enters a integer followed by a character (i.e. "8a") it isn't detected as an input error. Is there any way to remedy this?
EDIT: I've come up with a partial solution thanks to "David Rankin - ReinstateMonica".
I've changed the getline validation to only accept alphabetical characters, spaces, and periods using this bit of code:
while (!(names[i].find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ. ") == string::npos && !names[i].empty())) // check to make sure there are no invalid characters and names[i] isnt blank
{
cout << "Enter the student's name: ";
getline(cin, names[i]); // Allow for full name entry (first, middle, and last names)
if (!(names[i].find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ. ") == string::npos && !names[i].empty())) // another check, outputs error msg if getline value is invalid
cout << "Names must consist of letters, periods, or spaces. \n";
}
However I'm still stumped on how to validate the (!(cin >> score[i][j])) portion. Would it be possible to create a temporary char array for score[i][j] and then test if each element of the char array is a number? Or is there some easier method?
void getNameScore(string names[], double score[NGS_SIZE][S_SIZE], char grade[])
{
for (int i = 0; i < NGS_SIZE; i++)
{
cout << "Enter the student's name: ";
getline(cin, names[i]); // Allow for full name entry (first, middle, and last names)
double total = 0;
for (int j = 0; j < S_SIZE; j++)
{
cout << "Enter " << names[i] << "'s test " << j + 1 << " score: ";
while (!(cin >> score[i][j]) || score[i][j] < 0 || score[i][j] > 100) // input validation 0 to 100 as well as cin data type error checking
{
cout << "Enter a number between 0 and 100: ";
cin.clear(); // clear error flags when a non-integer value is entered in line 38
cin.ignore(10000, '\n'); // clear input buffer
}
total += score[i][j];
}
cout << endl;
cin.ignore(10000, '\n'); // clear getline buffer on line 32
double avg = total / S_SIZE;
if (avg >= 90)
grade[i] = 'A';
else if (avg >= 80)
grade[i] = 'B';
else if (avg >= 70)
grade[i] = 'C';
else if (avg >= 60)
grade[i] = 'D';
else
grade[i] = 'F';
}
}

Related

Looping assignment in C++

My assignment is utilizing loops. The program should accept input for the sales of 3 employees (Mary, Tom, and Chris). The flow should be as follows:
Initial? > number of sales to enter > enter sale amounts > display commission for sale at 17% > adds commission and sales to the respective variables >> continue until 'z' is input for inputSalesPerson >> display information
So I am trying to figure out why my return value for the tempComm variable isn't returning the correct value. If i was to enter 't' for variable inputSalesPerson it puts me into the switch case 't' no problem. Input number of sales and that works. But when I get to entering the salesAmount and then displaying commission it will not calculate correctly.
Also if I enter 'z' or 'Z' as the inputSalesPerson it will not end the program. I have a lot to go on this.
#include <iomanip>
#include <iostream>
using namespace std;
int main()
{
int salesT = 0, salesC = 0, salesM = 0;
double amountT = 0, amountC = 0, amountM = 0;
double commT = 0, commC = 0, commM = 0;
double commRate = (17/100);
int num_sales;
double salesAmount, totalSales, tempComm;
char inputSalesPerson;
do
{
cout << "Enter the sales person's initial (\"Z\" to quit): ";
cin >> inputSalesPerson;
while(inputSalesPerson != 't' && inputSalesPerson != 'T' && inputSalesPerson != 'm' && inputSalesPerson != 'M' && inputSalesPerson != 'c' && inputSalesPerson != 'C' && inputSalesPerson != 'z' && inputSalesPerson != 'Z')
{
cin.get();
system("cls");
cout << "Invalid input for employee. Please Input (T)om, (C)hris, (M)ary, or (Z) to End : ";
cin >> inputSalesPerson;
}
switch(inputSalesPerson)
{
case 't' :
case 'T' :
system("cls");
cout << "Enter the number of sales : ";
cin >> num_sales;
while(num_sales < 1 || num_sales > 5)
{
system("cls");
cout << "Invalid number of sales. Please enter a value between 1 and 5 : ";
cin >> num_sales;
}
salesT += num_sales;
for(int i = 0; i<num_sales; i++)
{
cin.get();
system("cls");
cout << "Enter the sale amount : ";
cin >> salesAmount;
while(salesAmount < 0)
{
cin.get();
system("cls");
cout << "Invalid sale amount. Please enter a positive amount : ";
cin >> salesAmount;
}
tempComm = salesAmount + (salesAmount * commRate);
cout << fixed << setprecision(2) << "Commission earned by tom on this sale is : " << tempComm << endl;
cin.get();
amountT += salesAmount + tempComm;
commT += tempComm;
totalSales += amountT;
}
break;
}
}while(inputSalesPerson != 'z' || 'Z');
return 0;
}
****EDIT****
Thank you for the information on single-step debugging. Thanks to that comment I was able to learn about using the debugging tool more in depth and that helped me get everything working a bit better.
I've commented your code at the areas that need fixing. Also, there's a problem with using cin.get() all over the place. I assume that you do this to discard the return character after each input. But if the standard input (cin) is empty when you call cin.get() it will block the program until something is input. This is what happens when you enter more than one num_sales:
for (int i = 0; i<num_sales; i++)
{
cin.get();
It handles the first fine, but on the second loop you get:
Enter the sale amount : 20
Commission earned by tom on this sale is : 23.40
// cin.get() blocks here, with no user instructions to enter the next sale amount
I've commented out all the cin.get(). It will still work the same because the cin operator >> discards whitespaces and newlines, so even if there is a \n newline character still in the buffer, the next time you do something like cin >> num_sales it will discard the newline anyway.
#include <iomanip>
#include <iostream>
using namespace std;
int main()
{
int salesT = 0, salesC = 0, salesM = 0;
double amountT = 0, amountC = 0, amountM = 0;
double commT = 0, commC = 0, commM = 0;
double commRate = (17 / 100.0); // Int divided by int will round to an int.
// commRate is 0.0. Divide by double instead (17 / 100.0)
int num_sales;
double salesAmount, totalSales = 0, tempComm; // totalSales needs to be initialised to
// zero, otherwise it holds a garbage value.
char inputSalesPerson;
do
{
cout << "Enter the sales person's initial (\"Z\" to quit): ";
cin >> inputSalesPerson;
while (inputSalesPerson != 't' && inputSalesPerson != 'T' && inputSalesPerson != 'm' && inputSalesPerson != 'M' && inputSalesPerson != 'c' && inputSalesPerson != 'C' && inputSalesPerson != 'z' && inputSalesPerson != 'Z')
{
//cin.get();
system("cls");
cout << "Invalid input for employee. Please Input (T)om, (C)hris, (M)ary, or (Z) to End : ";
cin >> inputSalesPerson;
}
switch (inputSalesPerson)
{
case 't':
case 'T':
system("cls");
cout << "Enter the number of sales : ";
cin >> num_sales;
while (num_sales < 1 || num_sales > 5)
{
system("cls");
cout << "Invalid number of sales. Please enter a value between 1 and 5 : ";
cin >> num_sales;
}
salesT += num_sales;
for (int i = 0; i<num_sales; i++)
{
//cin.get();
//system("cls");
cout << "Enter the amount for sale number " << i+1 << ": ";
cin >> salesAmount;
system("cls"); // I would put the clear here,
// Otherwise the user can't see the commission made by Tom
while (salesAmount < 0)
{
//cin.get();
system("cls");
cout << "Invalid sale amount. Please enter a positive amount : ";
cin >> salesAmount;
}
tempComm = salesAmount + (salesAmount * commRate);
cout << fixed << setprecision(2) << "Commission earned by tom on this sale is : " << tempComm << endl;
//cin.get();
amountT += salesAmount + tempComm;
commT += tempComm;
totalSales += amountT; // I think you mean to add salesAmount maybe?
}
break;
}
} //while (inputSalesPerson != 'z' || 'Z');
// Even if { this ^^^^} is false, ^^^ this is always
// 'Z' char will convert to bool, any non-zero value is true.
while (inputSalesPerson != 'z' && inputSalesPerson != 'Z');
return 0;
}

Input Validation for Vectors C++

I'm having a problem with the input validation of my do-while loop. My do-while loop only validates every other input
Here's an example:
Please Enter Grade 1: 101 (It does not accept input)
Invalid Rage. Please Enter Range 0-100.
Please Enter Grade 1: 101 (It accapts this input)
Please Enter Grade 2: 101 (It does not accept input)
Invalid Rage. Please Enter Range 0-100.
Please Enter Grade 2: 101 (It accapts this input)
Here's My Code:
for(vector<double>::size_type i = 0; i < 15; i++)
{
do
{
cout << "Please Enter Grade "<< i + 1 <<": " << flush;
cin >> gradesVector[i];
}
while(gradesVector[i] < 0.0 && gradesVector[i] > 100.0);
{
cout << "Invalid Rage. Please Enter Range 0-100:\n";
cout << "Please Enter Grade "<< i + 1 <<": " << flush;
cin >> gradesVector[i];
}
}
Try this:
int main() {
vector<double> gradesVector(15);
for(vector<double>::size_type i = 0; i < 15; i++) {
do {
cout << "Please Enter Grade "<< i + 1 <<": " << flush;
cin >> gradesVector[i];
} while(gradesVector[i] < 0.0 || gradesVector[i] > 100.0);
}
}
The statement while(gradesVector > 0.0 && gradesVector <= 100.0) is trying to check if the entire vector is greater than 0 or less than 100, which doesn't really make sense. You want to do something like while(gradesVector[i] > 0.0 && gradesVector[i] <= 100.0) (just make sure i is in the correct scope).
Edit: just get rid of the do-while loop and only use a for-loop. Inside the for-loop, add an if-statement: if(gradesVector[i] > 0.0 && gradesVector[i] <= 100.0). If the validation fails, use a break statement to exit the for-loop.

How to use a while loop with an array?

I've created a program that allows the user to enter 10 grades. I've used a while loop to store grades in the array, but if the user only has 5 grades to input, he can type done to exit the program.
After the loop has finished, it will then calculate and display. the highest grade, lowest grade, and the average grade within the array
Unfortunately, when the user types done, the program will display the rest of the grade lines that were not entered.
Can you help me find out how to stop the while loop from displaying the rest of unentered grades of the loop?
#include <iostream>
using namespace std;
int main()
{
const int SIZE = 10;
int grade[SIZE];
int count = 0;
int lowestGrade;
int highestGrade;
bool done = false;
cout << "This program is limited to entering up to 10 grades." << endl;
while ( grade[count] != done && count < SIZE)
{
cout << "Enter a grade #" << count + 1 << " or done to quit: ";
cin >> grade[count];
count++;
}
//LOWEST GRADE
lowestGrade = grade[0];
for (count = 0; count < SIZE; count++)
if (grade[count] < lowestGrade)
{
lowestGrade = grade[count];
}
//HIGHEST GRADE
highestGrade = grade[0];
for (count = 0; count < SIZE; count++)
{
if (grade[count] > highestGrade)
{
highestGrade = grade[count];
}
}
//AVERAGE GRADE
double total = 0;
double average;
for (int count = 0; count < SIZE; count++)
total += grade[count];
average = (total / SIZE);
cout << endl;
cout << "Your highest grade is: " << highestGrade << endl;
cout << "Your lowest grade is: " << lowestGrade << endl;
cout << "Your average grade is: " << average << endl;
system("pause");
return 0;
}
Here are two problems with your code.
First:
....
cout << "Enter a grade #" << count + 1 << " or done to quit: ";
cin >> grade[count];
count++;
....
The code above will attepmpt to read word "done" into integer variable, producing 0. Not what you want to do!
Second:
...
for (count = 0; count < SIZE; count++)
...
Code above will try to iterate over all possible elements (SIZE). However, you might have enetered less than that! You need to use count calculated in the previous loop as your boundary (and of course, use a different name for control variable in the loop).
There are a couple of things to unpack here.
Basically, the input you are retrieving is a char * and the >> operator is casting that to an int to fit into your array of grades.
Next what you are checking with grade[count] != done is if the integer in "grade" at the id "count" is not equal to the bool false. This will always return true in this case.
For your use case what you want to be checking is if your input is equal to the char * "done"
This cannot be happening in the predicate of the while loop because your grade array stores only int.
Therefore the simplest solution to the problem in my opinion, is to check whether the input is equal to "done".
If it is you want to set the done boolean to true
Otherwise we can try to cast it to an int and store that in the grades array.
Here is the revised loop:
while (!done && count < SIZE)
{
cout << "Enter a grade #" << count + 1 << " or done to quit: ";
string input = "";
cin >> input;
if (input == "done")
{
done = true;
}
else
{
grade[count] = stoi(input);
}
count++;
}
The following is somewhat outside the scope of the question, but an additionnal advantage to using stoi() is that it ignores input that is not a number, which will shield against someone entering invalid input like "potato". This is why I immediately cast the input into a string.
Use another variable to store the amount ofgrades the user entered. You also cannot store a string in your integer array:
std::string input = "";
while(count < SIZE)
{
cout << "Enter a grade #" << count + 1 << " or done to quit: ";
getline(cin, input);
if(input == "done")
break;
try
{
grade[count] = std::stoi(input);
count++;
}
catch(std::invalid_argument)
{
cout << "not a valid number\n";
}
}
int actualsize = count;
and then use this variable to abort your for loops:
for (int i = 0; i < actualsize; i++)
There are two simple ways to solve your problem:
You can read strings instead of integers and in case the read string is "done", break the loop, else, convert the read string to an integer, something as follows:
```
// rest of the code
int total_count = 0;
while (count < SIZE) {
cout << "Enter a grade #" << count + 1 << " or done to quit: ";
string temp;
cin >> temp;
if(temp == "done") {
break;
} else {
grade[count] = stoi(temp);
count++;
total_count = count;
}
}
// rest of the code
```
If you don't want to use strings, then, assuming grades will be non-negative, you can stop reading input when the user types a negative number, say "-1". So, you will need to do something as follows:
```
// rest of the code
int total_count = 0;
while (count < SIZE) {
cout << "Enter a grade #" << count + 1 << " or -1 to quit: ";
int temp;
cin >> temp;
if(temp == -1) {
break;
} else {
grade[count] = temp;
count++;
total_count = count;
}
}
// rest of the code
```
Also, don't forget to replace SIZE by total_count in rest of the loops i.e. the ones computing 'LOWEST GRADE', 'HIGHEST GRADE' and 'AVERAGE GRADE'.
NOTE: You will have to do #include <string> at the top as well, if you use the first option.

Program that finds Highest, Lowest, and Average of 5 number from an Array

My homework is to write a program that finds the highest, lowest, and average of 5 numbers in an Array that the user inputs. Here is my problem, the user does not have to enter all 5 numbers. But has to enter at least 2 numbers minimum.
I have the whole program done already I am having a problem with the beginning, below is my code where I am having a problem:
// Ask for name and explain program
cout << "Please enter your name: ";
cin >> name;
cout << endl;
cout << "Hi " << name << ", please enter up to 5 whole numbers." << endl;
cout << "I will find the HIGHEST, LOWEST, and AVERAGE number." << endl;
// Loop through users input
for (int i = 0; i < SIZE; i++)
{
cout << "Enter number " << (i + 1) << " : ";
cin >> number[i];
// Validate that the user has entered atleast 2 numbers
if (i >= 1 && i < 4)
{
cout << "Do you wish to enter another number (Y/N)? : ";
cin >> continue_game;
// Validate that the user only enters Y/N
while (continue_game != 'Y' && continue_game != 'y' && continue_game != 'N' && continue_game != 'n')
{
cout << "Please type in (Y/N): ";
cin >> continue_game;
}
// What happens if user chooses NO
if (continue_game == 'N' || continue_game == 'n')
{
i = 5;
}
// What happens if user chooses YES
else if (continue_game == 'Y' || continue_game == 'y')
{
i = i;
}
}
}
PROBLEM: If the user presses no after the 2nd number the remaining elements get a number asigned to them like : -8251616. Is there any way to make sure that the elements get assigned a zero or stay blank please help its due tomorrow and I can not figure it out.
SIZE = 5
Don't set i = 5 when the user says no. Just end the loop with a break; statement.
Also, the i = i; statement in the yes case is useless.
When you're getting the highest, lowest, and average values, make sure you only look at the values from 0 to i-1, so you don't access the uninitialized elemends of the array.
If you really want zeros you need to fill array with zeros:
int number[5] = {};
or
int number[5];
for (int i = 0; i < 5; ++i) {
number[i] = 0;
}
However this will give wrong output if the user enter less than 5 numbers. What you should do is to count how many numbers user entered and then use values from 0 to count - 1.
Advice, use break; instead of i = 5;.

Nested loops in C++ and user input

Pretty new here to programming, and I have an assignment where I need to achieve the following:
ask for total amount of people
get each of their names
allow user to enter up to 5 scores for each person
if there are less than 5 scores for a given person, inputting -100 will stop it
So far I have written this:
#include <iostream>
using namespace std;
int main() {
string personName;
int totalPerson, personScoreCounter;
double personGrade, personGradeTotal;
cout << "Input total amount of people: ";
cin >> totalPerson;
for (int person = 1; person <= totalPerson; person++)
{
cout << "Input name for person " << person << ": ";
getline(cin, personName);
cin.ignore();
while ( (personGrade != -100) && (personScoreCounter <= 5) )
{
cout << "Input up to 5 scores for " << personName << " (-100 to end): ";
cin >> personGrade;
if (personGrade >= 0 && personGrade <= 100) // valid range of scores
{
personGradeTotal += personGrade;
personScoreCounter++;
}
else
{
cout << "Input only scores from 0-100" << endl;
}
cout << "Input up to 5 scores for " << personName << " (-100 to end): ";
cin >> personGrade;
}
}
// calculate averages and other stuff in here.
return 0;
}
After getting their name, only the last cout inside the while loop seems to execute first, then it starts from the top and so on until the for loop hits the end depending on totalPerson. I know I'm missing a few things in here, probably in the order of operations and also the way I am executing my loops, but I just can't see it. Could any of you guys with experience in the language please give me any pointers as to what's happening here and how I can fix it? Thank you.
Inside your while group, you only want to use your cout line once (at the beginning looks good).
Your first check should be for ==-100 or similar, since as it is now, you'll get a "Input only scores from 0 to 100" message if you enter -100.
You should keep a cin.ignore(); call after each use of cin >> VARIABLE, since then you will drop the EoL character.
Example code:
#include <iostream>
using namespace std;
int main() {
int totalPerson;
cout << "Input total number of people: ";
cin >> totalPerson;
cin.ignore();
for (int person = 1; person <= totalPerson; person++)
{
int personScoreCounter=0;
double personGrade = -1, personGradeTotal=0;
string personName;
cout << "Input name for person " << person << ": ";
std::getline(cin, personName);
while ( (personGrade != -100) && (personScoreCounter < 5) )
{
cout << "Input up to 5 scores for " << personName << " (-100 to end): ";
cin >> personGrade;
cin.ignore();
if (personGrade == -100) {
break;
} else if (personGrade >= 0 && personGrade <= 100) {
personGradeTotal += personGrade;
personScoreCounter++;
} else {
cout << "Input only scores from 0-100" << endl;
}
}
// calculate averages and other stuff in here.
double avg = personGradeTotal / personScoreCounter;
cout << "Avg = " << avg << endl;
}
return 0;
}
Some of your variables also needed to move inside the for loop.
Additionally I changed the limits on the personScoreCounter to [0:4] rather than [1:5] - this way you can use it for averaging more easily.
You might also try cin.getline() instead of getline(std::cin , ... ):
int max_length = 30;
std::cin.getline(personName, max_length, '\n'); // \n is option termination.
This allows whitespaces in the input also.
http://www.cplusplus.com/reference/istream/istream/getline/