while loop to infinity when the input of a cin is a 'dot'

后端 未结 4 2014
执念已碎
执念已碎 2021-01-27 19:22

I am having trouble using the cin method to acquire a variable. When the input is a number there is no problem, but when it is a special character like a dot [.], the whileloop

相关标签:
4条回答
  • 2021-01-27 19:46

    There are several problems with your code. The first is that you don't verify that your input has succeeded; the correct condition for the while should be:

    while ( !cin || (*race < 1 || *race > 3) )
    

    As written, if the input fails (which is what is happening when you enter a '.', supposing that race has type int*), then *race contains its previous value, whatever that was.

    The second is that if you do get an error from cin, you don't clear it. Once the stream is in an error state, it stays that way until you explicitly clear it. If cin has failed, you need to execute:

    cin.clear();
    

    somewhere in the loop.

    The third is that if cin fails, you don't extract the character which made it failed, so that after clearing the error status, you need to extract it. Given the way you've structured your dialog, you probably want to ignore everything until the end of the line:

    cin.ignore( INT_MAX, '\n' );
    

    You may want to do this even if cin didn't fail, either in the loop (if entered because of the *race < 1 || *race > 3 condition), or in case of success. Alternatively, you may want to shift to reading lines, and ensure that the line only contains whitespace after the character you're interested in.

    This last solution is the one I would adopt, since it handles pretty much all of the above problems. So my code would look something like:

    //  return -1 on error in input,
    //  throw exception on (unexpected) end of file
    int
    getRace( std::istream& source )
    {
        std::string line;
        if ( !std::getline( source, line ) ) {
            throw std::ios_base::failure( "Unexpected end of file" );
        }
        std::istringstream tmp( line );
        int results;
        return tmp >> results >> std::ws && tmp.get() == EOF
            ? results
            : -1;
    }
    
    //  ...
    int race = -1;
    while ( race < 0 ) {
        std::cout << "What is your race\n"
                     "1. Human\n"
                     "2. Troll\n"
                     "3.  Zombie\n" << std::flush;
        race = getRace( std::cout );
        if ( race < 0 ) {
            std::cout << "Wrong choice" << std::endl;
        }
    }
    

    Note that by inputting through a line, you avoid any problems with resetting format errors, skipping erroneous input or resynchronizing in case of error.

    0 讨论(0)
  • 2021-01-27 19:53

    The other solution besides the one accepted is to clear the cin's failbit and ignore the last input like below:

    cout << "What is your race" <<endl<<"1.Human\n2.troll\n3.zombie"<<endl;
    cin >> *race;
    while(*race<1||*race>3)
    {
        // Clears the state of cin to be in good state
        cin.clear();
        // Ignores the last input read so that it's not read back again
        cin.ignore();
        system("cls");
        cout << "Wrong choice"<<endl<< "What is your race" <<endl<<"1.Human\n2.troll\n3.zombie"<<endl;
        cin >> *race;
    }
    
    0 讨论(0)
  • 2021-01-27 19:59

    Make race an char, then you will be able do to:

    while (*race < '1' || *race > '3')
    

    which is probably what you want to achieve.

    Explanation:

    When you cin >> into an int, it converts given ASCII string to integer value. . doesn't have an integer meaning, so it isn't read into race and failbit is set - further >>s are no-op, until you clear them. However, if you cin >> into char and compare it with other chars (well, their ASCII codes, actually), you will be able to check it without troubles.

    0 讨论(0)
  • 2021-01-27 20:02

    This example exactly reproduces your problem:

    #include <iostream>
    
    int main()
    {
        int i = 5;
        while (i < 1 || i > 3)
        {
            std::cin >> i;
        }
    }
    

    Here's what happens: When operator>> fails to read an integer (e.g. when you type a dot and press enter), whatever you typed stays in the stream, including the newline character. So in the next iteration of the while loop the next input is already there and since it's not a valid integer, the loop can never break. You need to make sure that, when operator>> fails, you empty the stream and clear all the error flags that got set.

    #include <iostream>
    #include <limits>
    
    int main()
    {
        int i = 5;
        while (i < 1 || i > 3)
        {
            if (!(std::cin >> i))
            {
                // clear streams internal error flags
                std::cin.clear();
                // ignore what's left in the stream, up to first newline character
                // or the entire content, whichever comes first
                std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题