While loop with try catch fails at bad cin input

后端 未结 6 1923
耶瑟儿~
耶瑟儿~ 2020-12-11 09:05

I can\'t seem to figure out why this falls into a loop after getting non-int input. I\'ve tried cin.flush(), which doesn\'t seem to exist, cin.clear(), which seems like it s

相关标签:
6条回答
  • 2020-12-11 09:35

    The most straight-forward (but not usually the easiest nor the fastest) way of dealing with validation of line-based input is to always read it line at a time. This way no extra whitespace (such as linefeed characters) is left unread in the buffer in any case, and discarding erroneous input is also quite automatic.

    // Infinite loop for retrying until successful
    while (true) {
        // Ask the user something
        std::cout << prompt;
        // Read the answer (one full line)
        std::string line;
        if (!std::getline(std::cin, line))
          throw std::runtime_error("End of input while expecting a value");
        // Put the line read into iss for further parsing
        std::istringstream iss(line);
        int val;
        // Read val from iss and verify that reading was successful and
        // that all input was consumed
        if (iss >> val && iss.get() == EOF) return val;
        std::cout << "Invalid input, try again!\n";
    }
    

    It is fun to make a BASIC style input function out of this:

    template <typename Val> void input(std::string const& prompt, Val& val) {
        // (the above code goes here, slightly adjusted)
    }
    
    int main() {
        int w;
        double l;
        input("Enter weight in kg: ", w);
        input("Enter length in m: ", l);
        std::cout << "BMI: " << w / (l * l) << std::endl;
    }
    

    Notes for the pedantics who were going to downvote me:

    • function input should be specialized for std::string
    • exceptions thrown by the input function should be caught in main
    0 讨论(0)
  • 2020-12-11 09:36

    It looks like you would be better off with iostream's native exceptions. Enable with

    cin.exceptions( ios::failbit );
    
    try {
       …
    } catch( ios_base::failure & ) {
      cin.clear();
      …
    }
    

    Never, ever throw an an object not derived from std::exception, and especially not a native type like char*.

    0 讨论(0)
  • 2020-12-11 09:37

    Exceptions should be used to handle exceptional, unexpected situations. Incorrect input from a user is neither unexpected nor exceptional -- it's more or less the norm. Personally, I tend to just ignore most bad input completely (when it can't be prevented). When (and only when) they enter something unusable repeatedly is it even worth pointing it out to them. As such, I'd tend to write the code something like:

    char ch;
    int attempts = 0;
    
    std::cout << "Please enter the first number: ";
    do { 
        cin >> ch;
        attempts++;
        if (attempts > 5)
            std::cerr << "The only allowable inputs are '0' through '9'\n";            
    } while (cin.good() && !isdigit(ch));
    int first_number = ch - '0';
    

    This reads the input as a character, so it's always removed from the input stream. Then it attempts to validate the input, and if it fails, attempts to read again. Of course, you might want/need to get a little more elaborate, such as reading an entire line, attempting to convert it to a number, and reading another line if that fails.

    0 讨论(0)
  • 2020-12-11 09:39

    Why are you doing this with exceptions? You're not going to kill the program on input, so you shouldn't throw an exception.

    Just print out your error message and attempt to read in again.

    0 讨论(0)
  • 2020-12-11 09:43

    You should think carefully what you want to do if user gives invalid input in this case. Usually in these cases the best solution is to read one line from the input and throw it away.

    Try putting cin.clear() and std::cin.ignore(std::numeric_limits<streamsize>::max(),'\n'); in your catch clause. cin.clear() clears the failure state in cin, and cin.ignore() throws away rest of the line waiting in the input buffer.

    (And yes, you probably should rethink your use of exceptions).

    0 讨论(0)
  • 2020-12-11 09:58

    My Problem was to block char input to a cin >> number

    This error caused an 'infinite' loop showing my prompt cout << prompt with no way of exit but kill the process ...

    The following shows what worked for me!

    ========================================

    double fi_trap_d()    // function to return a valid range double catching errors  
    {  
      double fi_game_sec;  
    //-------------------------------------------  
       do  
       {  
        fi_game_sec = -1;  
        cout << fi_game_sec_c;  
    
            //------------------------------  
        cin.ignore();  // (1)
            //------------------------------  
    
        try  
        {  cin >> fi_game_sec;  cin.clear(); }  // (2)
            catch (...)  //out_of_range)  
          {  
            fi_game_sec = -1;  
            cout << " Dis am an error!\n";  
                         // just loop back as we asked for a number  
          }  
        } while (fi_game_sec < 1);  
    //-------------------------------------------  
      return fi_game_sec;  
    }  
    

    ========================================

    Despite trying the " Dis am an error! " has NEVER shown up.

    The key was (1) & (2) !

    0 讨论(0)
提交回复
热议问题