0

I have a program which has the ability to reject user input if a char is entered instead of an int, and this works almost perfectly - anything entered that isn't a number is being rejected.

However, all of these cins need to accept any value between a minimum and a maximum, but I can't get it to work. The code below shows my efforts so far, but there's a slight bug. If a char is entered, followed by an int that is out of range, and another char is entered (I like to test rigorously - I mean, who knows what could happen if an actual end user came across the problem) the program throws the final value of mortgageTerm out as 0.

Could anyone tell me where I'm going wrong and give me any pointers to help me fix it? Thanks in advance to anyone who's able to help me solve my problem!

int mortgageTerm;
string line;
cout << "Mortgage term (1 - 40 years) : ";
while (!(cin >> mortgageTerm))
{
    cout << "That's not a valid choice! Try again : ";
    cin.clear();
    cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
while (getline(cin, line))
{
    stringstream linestream;
    if (!linestream >> mortgageTerm)
    {
        cout << "Input was not a number! Try again : ";
        cin >> mortgageTerm;
        continue;
    }
    if ((mortgageTerm <= 0 || mortgageTerm > 40))
    {
        cout << "Input out of range. Try again : ";
        cin >> mortgageTerm;
        continue;
    }
    char errorTest;
    if (linestream >> errorTest)
    {
        cout << "Invalid input. Try again : ";
        cin >> mortgageTerm;
        continue;
    }
    break;
}
cout << mortgageTerm;
Rob Dudley
  • 25
  • 8
  • Pretty much a dupe of [this](http://stackoverflow.com/questions/10828937/how-to-make-cin-take-only-numbers) but it does not do the range checking(fairly trivial to add though). – NathanOliver Apr 10 '17 at 19:13
  • @NathanOliver yeah that's the one that's currently in the program, I was bug checking when I realised I'd gotten rid of one problem and introduced another in the form of it not being able to do range checking – Rob Dudley Apr 10 '17 at 19:16

2 Answers2

0

Just check for the minimum and maximum in the same condition where you check if it was able to be converted into an int, using ||, in a condition the expressions are checked left to right in order, so the first did its work already when you evaluate the second and mortageTerm will have the value.

Edited to address comments.

int mortgageTerm;
cout << "Mortgage term (1 - 40 years) : ";

while (!(cin >> mortgageTerm) ||
       mortageTerm < 1        ||
       mortgageTerm > 40      )
{
    cout << "That's not a valid choice! Try again : ";
    cin.clear();
    cin.ignore(std::numeric_limits<streamsize>::max(), '\n');
}

// If you are concerned about extra input after the number and want to clear the input stream
// cin.ignore(std::numeric_limits<streamsize>::max(), '\n');
Christopher Pisz
  • 3,757
  • 4
  • 29
  • 65
  • This would allow you to enter `39a` which would leave the `a` in the buffer and that will mess up subsequent reads. – NathanOliver Apr 10 '17 at 19:19
  • This works to an extent, but now the problem I'm facing is the if the char entered has more than one character (e.g. "one"), the error message repeats itself for the number of characters in the input: Mortgage term (1 - 40 years) : one That's not a valid choice! Try again : That's not a valid choice! Try again : That's not a valid choice! Try again : – Rob Dudley Apr 10 '17 at 19:30
0

You're almost there. Your first issue is your first while loop is not needed at all. Then we just need to tweak the second loop to make sure that all the input read was used in the value you get. We can also simplify it by using a single error statement, Making those changes gives you

int mortgageTerm;
string line;
cout << "Mortgage term (1 - 40 years) : ";
while (getline(cin, line)) // consume all input given
{
    stringstream linestream(line); // you have to construct the stream from the string here
    linestream >> mortgageTerm; // try and read the data
    if (!linestream.eof() || mortgageTerm <= 0 || mortgageTerm > 40)
    {
        // either there is input left in linestream or the value is not in range
        cout << "Invalid input. Try again : ";
    }
}
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • @RobDudley No problem, glad to help. I did notice I had a bug in the code tough and I've update it. – NathanOliver Apr 10 '17 at 20:00
  • I hope he understand why that works. The difference between operator >> and getline, and why the check for EOF is there. If you are trying to treat an entire line as a single value, you can use getline this way and EOF to check that there is no extra data. However, many people like to give all their input at once or from a file and using operator >> it parses all those tokens. For example "9 0.1 ABC" could be three fields on the same line and operator >> would be called on each of them. – Christopher Pisz Apr 10 '17 at 20:34