3

I am trying to read an integer from terminal. Here's my code:

int readNumber()
{
    int x;
    std::cin >> x;
    while(std::cin.fail())
    {
        std::cin.clear();
        std::cin.ignore();
        std::cout << "Bad entry. Enter a NUMBER: ";
        std::cin >> x;
    }
    return x;
}

Whenever I run this code I get:

Type in the number for the newsgroup that shall be deleted:
joöä
Bad entry.  Enter a NUMBER: Bad entry.  Enter a NUMBER: Bad entry.  Enter a NUMBER: Bad entry.  Enter a NUMBER: Bad entry.  Enter a NUMBER: Bad entry.  Enter a NUMBER: 8

Why does it write "bad entry" multiple times? If I remove std::cin.clear(); or std::cin.ignore();, the program just keeps writing Enter a NUMBER: Bad entry. Can anyone explain why it does that?

cf-
  • 8,598
  • 9
  • 36
  • 58
user2975699
  • 585
  • 1
  • 9
  • 22

4 Answers4

3

This is actually a partial duplicate of an old question, but it is phrased differently enough that I will address it briefly here.

  1. You get infinite printing without those two lines, because cin.clear() is required to clear the error flag that cin.fail() is reading. See the linked question for details.
  2. Why does it still print more than once when those lines are there? When you do std::cin >> x;, your code will read the first character in your input, and attempt to parse it as an int. It will fail, and then in the next iteration of the loop, it will attempt to parse the next character and fail again. For each failure (that is, each character in your input), it will print Bad entry. Enter a NUMBER:. If you type some bad input with fewer characters, you will see what I mean.

There are multiple ways to fix this problem, but one simple fix is to read the entire input into a string, and try to parse it, instead of using cin directly into an int. Here is some sample code which needs to be compiled with one of the various compiler-dependent flags for C++11. I have tested it with your input and it appears to achieve the effect you desire.

#include <iostream>
#include <string>
#include <stdexcept>

bool tryParse(std::string& input, int& output) {
    try{
        output = std::stoi(input);
    } catch (std::invalid_argument) {
        return false;
    }
    return true;
}
int main(){
    std::string input;
    int x;

    getline(std::cin, input);

    while (!tryParse(input, x))
    {
        std::cout << "Bad entry. Enter a NUMBER: ";
        getline(std::cin, input);
    }
    return x;
}
Community
  • 1
  • 1
merlin2011
  • 71,677
  • 44
  • 195
  • 329
1

Try this anyway... As I type this directly into stackoverflow might have compilation error

int readNumber()
{
   int x=0; 
   while(true)
   {
        std::cin >> x;
        if(!std::cin.fail()){
            break;
        }
        else{
            std::cout << "Bad entry. Enter a NUMBER: " << std::endl;
            std::cin.clear();
            std::cin.ignore( std::numeric_limits<streamsize>::max(), '\n' );
        }
    }
 }
rockinfresh
  • 2,068
  • 4
  • 28
  • 46
1
int main() {
    char* input;
    std::cin >> input;
    int x = std::atoi(input);
    while(x == 0 && strcmp(input, "0") != 0) {
        std::cout << "Bad entry. Enter a NUMBER: ";
        std::cin >> input;
        x = std::atoi(input);
    }
    return x;
}

Notice that when you have 3 non-int chars, it will repeat 3 times, when you have 5, it's repeated 5 times. It's because cin is tries the first char, fails, try the second char, fails and so on until all your input chars are parsed.

jianweichuah
  • 1,417
  • 1
  • 11
  • 22
0

cin.fail() returns true if the last cin command failed, and false otherwise.

Try this instead:

    while(1)
    {
        std::cin >> x;
        if (!std::cin.fail()) break;
        std::cin.clear();
        std::cin.ignore(10000,'\n');
        std::cout << "Bad entry. Enter a NUMBER: ";
    }
Engineer2021
  • 3,288
  • 6
  • 29
  • 51