I have a C++ assignment to complete over the next couple of weeks. The task is to
- read a line of input containing 3 scores between 0 and 89, and read a name from the same line.
- Convert those scores to marks and give each student a grade depending on their marks.
My first cut has done all of this (albeit in a messy way) and now I am thinking about invalid input. The input I am really struggling with is how to deal with a user who keys in just the name: i.e. Smith, B.
Or what if one of the marks contains letters i.e. 10 c 20 Smith, B.
At the moment my 开发者_StackOverflow中文版program just reads it as an int I think and tries to carry on anyway - I want it to treat this as a string and output: "Bad input line: Smith, B."
Is there a way that experienced programmers deal with things like this? I don't want to create a mess of if statements everywhere in my code as that makes it hard to follow.
#include <iostream>
#include <string>
using namespace std;
//function to check input validity
bool validateInput(int mark1, int mark2, int mark3, string name);
//function to check the scores
int checkScores(int mark1, int mark2, int mark3);
int main()
{
int mark1 = 0, mark2 = 0, mark3 = 0;
string name;
cin >> mark1 >> mark2 >> mark3;
getline (cin, name);
// continue if the input is valid
if (validateInput(mark1, mark2, mark3, name) == true)
{
checkScores(mark1, mark2, mark3);
}
else cout << "Bad Input Line: \t" <<
mark1 << " " << mark2 << " " << mark3
<< " " << name << endl;
return 0;
}
bool validateInput(int mark1, int mark2, int mark3, string name)
{
//return false if the scores are not valid
if (mark1 < 0 || mark1 > 50 || mark2 < 0 || mark2 > 50 || mark3 < 0 || mark3 > 50)
{
return false;
}
return true;
}
int checkScores(int mark1, int mark2, int mark3)
{
int mark1Mark = 0, mark2Mark = 0, mark3Mark = 0;
string candidateType;
//return the mark for the mark1 test
if (mark1 <= 10) mark1Mark = 1;
else if (mark1 <= 20) mark1Mark = 2;
else if (mark1 <= 30) mark1Mark = 3;
else if (mark1 <= 40) mark1Mark = 4;
else if (mark1 <= 50) mark1Mark = 5;
//return the mark for the mark2 test
if (mark2 <= 10) mark2Mark = 1;
else if (mark2 <= 20) mark2Mark = 2;
else if (mark2 <= 30) mark2Mark = 3;
else if (mark2 <= 40) mark2Mark = 4;
else if (mark2 <= 50) mark2Mark = 5;
//return the mark for the mark3 test
if (mark3 <= 10) mark3Mark = 1;
else if (mark3 <= 20) mark3Mark = 2;
else if (mark3 <= 30) mark3Mark = 3;
else if (mark3 <= 40) mark3Mark = 4;
else if (mark3 <= 50) mark3Mark = 5;
if (mark1Mark >= 5 && mark2Mark >= 5 && mark3Mark >= 5)
candidateType = "Exceptional";
else if (mark3Mark >= 2 && mark2Mark >= 2 && mark1Mark >= 2)
candidateType = "Pass";
else candidateType = "Fail";
cout << mark1Mark << " " << mark2Mark << " " << mark3Mark << " " << candidateType;
}
You've done pretty good so far. I'll suggest you don't use
cin >> mark1 >> mark2 >> mark3;
cin is notorious for no error checking, leaving things in the input, not getting the newline, etc...
Instead get the entire line all at once into a string with getline and parse it out with error checking using a std::stringstream
string input = "";
getline(cin, input);
stringstream instream(input);
if (instream >> mark1) {
// number ok do something
}
else {
// not a number in the input
// report it
}
Failure to convert ill-formed input needs to be checked using cin.fail()
as shown here.
int x;
cout << "enter choice:";
cin >> x;
while (x < 1 || x > 4)
{
cout << "Invalid choice, try again:";
cin >> x;
// not a numeric character, probably
// clear the failure and pull off the non-numeric character
if (cin.fail())
{
cin.clear();
char c;
cin >> c;
}
}
cin
is helping you to validate the input upfront here, you just have to check what it tells you.
If you want to output the entire bad line on error, you could restructure the code like this to capture it and then parse, preserving the input for error display:
string rawLine;
getline(cin, rawLine);
stringstream lineStream;
int mark1, mark2, mark3;
string name;
lineStream >> mark1 >> mark2 >> mark3 >> name;
if (lineStream.fail() || // malformed input
!validateInput(mark1, mark2, mark3, name))
// input valid but semantically incorrect
{
cerr << "bad line :" << rawLine << endl;
}
Thanks for all the help here. I am using stringstream now to get the entire line and checking inputs against that - much more reliable. My main() now looks like this:
int main()
{
string input;
getline(cin, input);
stringstream instream(input);
int mark1 = 0, mark2 = 0, mark3 = 0;
string name;
instream >> mark1 >> mark2 >> mark3 >> name;
// continue if the input is valid
if (validateInput(mark1, mark2, mark3, name) == true)
{
checkScores(mark1, mark2, mark3);
}
else cout << "Bad Input Line: \t" << input << endl;
return 0;
}
精彩评论