C++ error with input validation in a do-while loop

跟風遠走 提交于 2020-01-06 03:46:10

问题


I'm creating a very simple number guessing game for a school project and am having trouble with the repeating main menu. I created it using a do-while loop and the problem I'm having is that the menu selection variable is an int, and so when I (or the user) enters a non-int input by accident when selecting from the menu the }while(condition) at the end of the main loop can't catch it and the program repeats infinitely. Conversely if you enter an invalid int at menu selection the program catches it displays the "invalid input" message and then repeats the main menu.

It's kind of hard to explain in writing exactly what I mean so here is the source code with relevant lines denoted with an asterisk. I'm saving as .cpp and am compiling in linux using g++ -ansi -pedantic -Wall -Werror The teacher has forbidden hardcoding in conditional statements hence the global constants.

#include <iostream>
#include <ctime>
#include <cstdlib>
using namespace std;
const int PLAY = 1, HIGH_SCORE = 2, EXIT = 3;
const char YES = 'y', NO = 'n';

int main()
{
// Randomly generated value
  int randomNumber;
// User input
  int userGuess, menuChoice;
  char repeat;
// Calculated value
  int numberOfGuesses;
// Place-holder values (to be replaced by calculated values)
  int score1 = 1000, score2 = 2000, score3 = 3000;

  cout << endl << endl;
  cout << "Greetings! This is a number guessing game where I think of" << endl
       << "a whole number between one and ten and you try to guess it!" << endl
       << "You can guess as many times as you like, so don't be afraid" << endl
       << "to use trial and error, but your score is based on the " << endl
       << "number of guesses you make (the lower the better) so don't " << endl
       << "guess too haphazardly. Remember, only guess whole numbers!" << endl
       << endl;

  do
  {
    cout << endl << "Main menu." << endl
         << "1. Play game" << endl
         << "2. Display high scores" << endl
         << "3. Exit game" << endl
         << "Please select an option: ";
    cin >> menuChoice;

    if (cin.fail()){     
      cout << "Please enter a valid choice" << endl;
      continue;
    } 
    cin.ignore();

    switch(menuChoice)
    {
      case PLAY:
      do
      {
        unsigned seed = time(0);
        srand(seed);
        randomNumber = 1 + rand() % 10;

        cout << endl << "Press enter when you're ready to begin!";
        cin.ignore();
        cout << "Ok I thought of one!" << endl << endl;

        numberOfGuesses = 0;

        do
        {
          numberOfGuesses++;

          cout << "Enter your guess: ";
          cin >> userGuess;
          cin.ignore();

// Check user's guess
          if (userGuess == randomNumber)
            cout << "Correct! That was impressive!" << endl << endl;
          else if (userGuess < randomNumber)
            cout << "Not quite, you guessed low." << endl << endl;
          else if (userGuess > randomNumber)
            cout << "Not quite, you guessed high." << endl << endl;
        }while (userGuess != randomNumber);

        cout << "Your score for this game was " << numberOfGuesses << endl;

// Determine if a high score was beaten
        if (numberOfGuesses <= score1)
        {
          score3 = score2;
          score2 = score1;
          score1 = numberOfGuesses;
          cout << "That's a new all time high score!" << endl;
        }
        else if (numberOfGuesses <= score2)
        {
          score3 = score2;
          score2 = numberOfGuesses;
          cout << "That's a new high score!" << endl;
        }
        else if (numberOfGuesses <= score3)
        {
          score3 = numberOfGuesses;
          cout << "That's a new high score!" << endl;
        }
        else
        {
          cout << endl; 
        }

        cout << "Would you like to play again? y/n: ";
        cin.get(repeat);
        cin.ignore();

        while (tolower(repeat) != YES && tolower(repeat) != NO)
        {
          cout << endl;
          cout << "Sorry, that is an invalid choice." << endl
               << "Please enter 'y' for yes or 'n' for no: ";
          cin.get(repeat);
          cin.ignore();
        }
      }while (tolower(repeat) == YES); 
        break;

      case HIGH_SCORE:
      cout << endl << "High Score 1: " << score1 << endl
           << "High Score 2: " << score2 << endl
           << "High Score 3: " << score3 << endl << endl;
      cout << "Press enter to continue. ";
      cin.ignore();
        break;

      case EXIT: 
      cout << endl << "Thanks for playing, I'll see you next time!" << endl << endl;
        break;

      default:
      cout << endl << "That is an invalid selection, please enter '1', '2' or '3'"
           << endl;
        break;
    } 
  }while (menuChoice != EXIT);

  return 0;
}

Code Edited in regards to current answer.

Please let me know if you need anymore information, thanks in advanced!


回答1:


Use cin.fail() like this (instead of just cin >> menuChoice;) (modelled after this post):

cin >> menuChoice;
if (cin.fail()) {
  cout << "Please enter a valid choice" << endl;
  cin.clear();
  cin.ignore();
  continue;
}
//Remove the cin.ignore() at this place!

For more detailed info, see this SO thread




回答2:


Use a do-while to ensure that the loop body will run at least once.

By using a do-while and prompting a user outside the loop you assume the user wants to play the game once which may not be the case.

A cleaner approach IMO would be use a while loop. Display the menu outside the loop and at the end of the loop. The user will have the choice to exit immediately.

 cout << "Greetings.....
 cout << menu
 // Get menuChoice input here.    
    while(menuChoice != EXIT){
        ...

         cout << menu //reprompt at end to continue or exit cleanly
         // Get menuChoice input here
    }

Input Validation is a perfect time to use a do-while

do{
   if(!cin){
      cout << "Invalid input"
      cin.clear()
      cin.ignore(numeric_limits<streamsize>::max(), '\n');
   }
}while(!(cin >> menuChoice)) // This gets console input. If fail, loop.
  • Use numeric_limits<streamsize>::max() to completely clear the buffer.
  • Use cin.clear() to reset the fail flag on cin so it wont always be false.

cin.fail() is fine. However some would consider !cin more natural.



来源:https://stackoverflow.com/questions/21368810/c-error-with-input-validation-in-a-do-while-loop

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!