7

Say I have a program that takes in integers. How do I stop the program from falling apart if the user enters an out of range number, or a letter or something?

Eamon Moloney
  • 1,853
  • 4
  • 19
  • 22

5 Answers5

4

The cin's base class is std::basic_istream. The input stream indicates a recoverable error in case it cannot extract the requested data from the stream. In order to check for that error bit, std::basic_istream::fail() method must be used — it returns true if there was a failure or false if everything is alright. It is important to remember that if there is an error, the data is left in the stream and, of course, the error bit(s) must also be cleared using std::basic_istream::clear(). Also, a programmer must ignore incorrect data, or otherwise an attempt to read something else will fail again. For that purpose, std::basic_istream::ignore() method can be used. As for the valid range of values, it must be checked manually. Okay, enough theory, here is a simple example:

#include <limits>
#include <iostream>

int main()
{
    int n = 0;

    for (;;) {
        std::cout << "Please enter a number from 1 to 10: " << std::flush;
        std::cin >> n;

        if (std::cin.fail()) {
            std::cerr << "Sorry, I cannot read that. Please try again." << std::endl;
            std::cin.clear();
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            continue;
        }

        if (n < 1 || n > 10) {
            std::cerr << "Sorry, the number is out of range." << std::endl;
            continue;
        }

        std::cout << "You have entered " << n << ". Thank you!" << std::endl;
        break;
    }
}

Hope it helps. Good Luck!

4

I prefer reading the input as strings, and then sanitizing them with boost::lexical_cast<>:

#include <boost/lexical_cast.hpp>
#include <iostream>
#include <string>

int main () {
  std::string s;
  while( std::cin >> s) {
    try {
      int i = boost::lexical_cast<int>(s);
      std::cout << "You entered: " << i << "\n";
    } catch(const std::bad_cast&) {
      std::cout << "Ignoring non-number: " << s << "\n";
    }
  }
}

Postscript: If you are allergic to Boost, you can use this implementation of lexical_cast:

template <class T, class U>
T lexical_cast(const U& u) {
  T t;
  std::stringstream s;
  s << u;
  s >> t;
  if( !s )
    throw std::bad_cast();
  if( s.get() != std::stringstream::traits_type::eof() )
    throw std::bad_cast();
  return t;
}
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • Catch-all clause w/o re-throw is no-no. –  Nov 08 '12 at 03:24
  • Can you explain that, or provide a citation? – Robᵩ Nov 08 '12 at 03:25
  • 2
    Yes, implementation of the run-time may depend on exception mechanism. For example, a thread cancellation. If you don't know what you catch, you may as well catch ABI-specific exception and... well, the program will not behave as you expect. So this is sort of a taboo, even though absolutely legal C++ code. Ulrich wrote a nice long description with examples as well, see here — http://udrepper.livejournal.com/21541.html –  Nov 08 '12 at 03:29
  • I'm pretty sure `lexical_cast` streams out remaining whitespace before testing for `eof()`. At least, in all the places I've implemented `lexical_cast` I have. :) ([eg](http://stackoverflow.com/a/1243435/87234)) – GManNickG Nov 08 '12 at 04:37
  • @GManNickG - I'm pretty sure `boost::lexical_cast` [doesn't](http://ideone.com/RUeGJ5). – Robᵩ Nov 08 '12 at 14:57
1

something like this should do you need to clear the buffer after checking aswell if i remember right

 if (cin.fail())
    {
      cout<<"need to put a number"<<endl;
      cin.clear();
      cin.ignore();
     }  
user23012
  • 219
  • 2
  • 16
1

If You dont want to add libraries to your code you could also use do..while() statements. in your do while you will ask for user input and then receive it to your variable then in the while part you will be able to check that this is the data you are expecting if not continue to ask for the data.

just another option ....even though the answer already mentioned should work more than adequately

Big_t
  • 41
  • 5
1

You can use the following code for simplest and fast checking of valid input in int :

#include "stdafx.h"

#include <iostream>
using namespace std;

int main()
{

    int intb;
    while( !( cin>>intb ) ){
        cin.clear ();
        cin.ignore (1000, '\n');
        cout<<"Invalid input enter again: "<<endl;

    }
    cout<<"The value of integer entered is "<<b<<endl;

        return 0;
}

The while loop keeps on iterating until it gets the right input. cin.clear() changes the error control state. cin.ignore() removes clear the input stream so that new input can be taken again. If not done thw while loop will be in infinite state.

Aadil Imran
  • 105
  • 1
  • 5