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

后端 未结 4 2015
执念已碎
执念已碎 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.

提交回复
热议问题