0

In my c++ code, I would like to validate my user input to be an int between 1,10 using a do while loop. I am able to validated for integers outside of the range. However if user inputs a float or a letter, it becomes an infinite loop. My idea is to add a condition in my while loop for if the input is not an integer to keep asking for input. the CAPITAL letters is where I am having trouble.

#include <iostream>
using namespace std;


int main(){
    cout << "Welcome, ";
    int steps;
    int count=0;
    do{
        cout << "How many? \n";
        cin >> steps;
        IF (STEPS IS NOT INTEGER==TRUE){
            COUNT=1;
        }
        if (steps <1)
        {
            cout << "not enough...\n";
        }
        if (steps > 10){
            cout << "too many steps.\n Please pick a lower number of steps.\n\n";
        } 
      } while (steps < 1|| steps >10 || COUNT==1);
    
    //doing stuff with valid input


    return 0;
}

Essentially I am trying to add another condition that just returns a boolean. and if the boolean implies that the input is not valid, then it reassigns count to make sure the do while loops continues until the input is valid.

The problem i am working on asks for a max and min steps, since all of them were having a similar problem i tried to simplify it and forgot some of the edits.

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
Lizzy
  • 39
  • 7
  • 3
    You declared that `steps` is an integer, it can't be anything other then that. If you want to make sure the user inputs a number into the console, then you need to use strings and parse it to see if it is in the expected format. – NathanOliver Jul 27 '20 at 18:36
  • 1
    https://stackoverflow.com/questions/1283302/user-input-of-integers-error-handling Check out the top answer for this question. You don't necessarily need to do parsing to make sure you have an integer. – MPops Jul 27 '20 at 18:37
  • `cin >> steps;` is reading into an `int`eger variable. By definition, the result will be an `int`.. – Jesper Juhl Jul 27 '20 at 18:38
  • 4
    @MPops The problem with solutions like that is that they do not keep the stream clean. If I enter `1a`, `1` will get stored in `steps` and the `a` would be left in the stream to mess with the next input. – NathanOliver Jul 27 '20 at 18:54
  • Think that as `steps` is declared `int`, it will never receive a non-`int` value ! –  Jul 27 '20 at 19:29
  • Note that the user cannot “[input] a float” when the code is extracting an `int`. The user types **text**, and the stream extractor tries to convert that text to an integer value. If it can’t convert the text you get an error. If it can convert it you get the result, and if their’s any leftover text it’s still there. – Pete Becker Jul 27 '20 at 21:36
  • @NathanOliver I'm aware, I just don't like answering every detail of a person's question. They would have stumbled on that issue themselves which would have made them research this next problem. imo this way they learn better instead of just thinking "guess I should always put a `.ignore()` after fail checking because that's just how we do it". Instead they will stumble into the actual problem and correct it themselves after research :) – MPops Jul 27 '20 at 21:51

2 Answers2

2

You can check whether the input failed, i.e. the user entered something that could not be read as an int like this:

if (cin.fail()) {  // in place of IF (STEPS IS NOT INTEGER==TRUE)
  cin.clear();      
  cin.ignore();
  cout << "not an integer, try again\n";
  continue;
}

This avoids the need for the COUNT variable.

Also, your while condition doesn't appear to match the checks inside the loop. What happens when step is either 9 or 10? You should be consistent with the checks inside the loop.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • 1
    you also need to `ingnore` the left over user input, no? I have to admit I never can remember the "right way" – 463035818_is_not_an_ai Jul 27 '20 at 18:43
  • @idclev463035818 Yes, I think you're right, edited. I find it hard to keep track of the right way as well :) – cigien Jul 27 '20 at 18:44
  • its not something I usually do in production code, only in code that has to work somehow ;). – 463035818_is_not_an_ai Jul 27 '20 at 18:45
  • It works! ty, just to make sure i understand this part. cin.fail tells me if input doesnt match with declaration of steps. cin.clear just deletes it, and ignore will ignore everything else in the user input that triggered the fail. and then continue just makes the code move forward.yes? – Lizzy Jul 27 '20 at 18:50
  • @Lizzy Yes, pretty much. `fail` will signal whether `cin` failed for *any* reason at all. – cigien Jul 27 '20 at 18:51
  • 1
    `cin.ignore();` here can be trouble. For example A3A will fail. The A will be ignored, and the next try will find the 3 and accept it. If this is the behaviour you want, cool beans. If not, you probably want to read into a `std::string` to get the whole A3A token out of the stream. – user4581301 Jul 27 '20 at 18:55
  • 2
    There's no authentic alternative to reading into a string. The problem is that it's beyond many newbies to write the code that checks the string and then converts. – john Jul 27 '20 at 19:10
  • @cigien I am now working on generalizing the code, and so now it asks for min and max steps as well as how many steps. The while (steps max), if the min is 0 or lower, the input fail is triggered but it also exits the program instead of asking the user again. If min is 1, 2, 3, ect. the program works as intended. Why is that? – Lizzy Jul 27 '20 at 20:21
  • @Lizzy That sounds like a separate question. If this question is answered, then accept it, and post a new question. – cigien Jul 27 '20 at 20:56
  • @john hm, you're saying even using `cin.ignore(numeric_limits::max(), '\n')` isn't acceptable? genuine question. I guess I was taught to always flush the entire buffer when a failure occurs. I never even considered first reading into a string and parsing. – MPops Jul 27 '20 at 21:57
  • 1
    @MPops depends on the input requirements. Ignoring to linefeed may not help if there are multiple tokens on the line. – user4581301 Jul 27 '20 at 21:59
  • 1
    @user4581301 Ah, so if you have a comma separated or really anything more complex than just a new-line separated input, it's better to load the whole thing and proceed – MPops Jul 27 '20 at 22:07
  • 1
    Yes. For the trivial stuff `ignore` works, but it runs out of usefulness really fast. In this case we don't really know what the asker is receiving for input. Probably ignore will be fine. Any instructor who throws an XML or JSON parser or god forbid a C++ parser at new students is a complete bastard. – user4581301 Jul 27 '20 at 22:13
  • 1
    `ignore()` with no parameters skips only 1 char. Typically you want to skip more chars than that, so you have to specify how many chars to skip. `ignore()` skips chars until the specified count have been skipped, the specified delimiter is reached, or EOF is encountered. If you don't know how many chars to skip, `numeric_limits::max()` can be used as a special case to not check the count at all, only the delimiter and EOF. In any case, the delimiter can be a space, a line break, whatever you need based on your particular input requirements. – Remy Lebeau Jul 28 '20 at 00:36
  • @RemyLebeau, I tried reading the documentation, but for this. I understand how the specific line works. however when i add that into the above code as, `std::cin.ignore(std::numeric_limits::max())`, the code doesnt loop and ask the user for input again. how would i fix this? – Lizzy Jul 28 '20 at 13:25
  • @Lizzy you are not specifying a delimiter, so it defaults to EOF. By using `numeric_limits` to turn off count checking, and waiting for EOF, the only way for `ignore()` to exit is if the user aborts input on the terminal. Add a delimiter to avoid that, ie `std::cin.ignore(std::numeric_limits::max(), '\n')` – Remy Lebeau Jul 28 '20 at 14:42
0

You could use the ! operator.

For example:

if ( !(std::cin >> steps) )
        {
          std::cin.clear();
          std::cin.ignore();
          std::cout << "Incorrect entry. Try again: ";
        }

Also consider not using using namespace std;.

Geno C
  • 1,401
  • 3
  • 11
  • 26