Yiannis
Yiannis

Reputation: 950

Integer input validation - Rejecting non integer input and requesting another in C++?

This function keeps getting called in another function inside a while-loop while valid_office_num is false. The problem is that if the input begins with a digit but is followed by other invalid characters (e.g. 5t) it takes the digit part and accepts that as a valid input. I want it to consider the whole input and reject it so it can ask for another one. I thought I could use getline() but then I cannot use cin.fail(). How could I implement this behavior?

I forgot to mention I am very new to C++, I have only learnt the basics so far.

(To be clear the desired behavior is to reject anything that contains anything other than digits. This is not an integer range check question. If it is NOT an integer, discard it and request another one)

//Function to read a valid office number, entered by user
int read_office_num()
{
    //Declaration of a local variable
    int office_num;

    //Read input
    cin >> office_num;

    //Check if input was valid
    if (cin.fail())
    {
        //Print error message
        cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n";
        //Clear error flags
        cin.clear();
        //Ignore any whitespace left on input stream by cin
        cin.ignore(256, '\n');
    }
    else
    {
        //Office number entered is valid
        valid_office_num = true;
    }

    return office_num;
}

Upvotes: 2

Views: 3091

Answers (6)

Jonathan Mee
Jonathan Mee

Reputation: 38909

You should probably just look at the number of input characters that are left in cin. You can do that with in_avail

Your function will probably end up having a body something like this:

//Declaration of a local variable
int office_num;

//Read input and check if input was valid
for (cin >> office_num; cin.rdbuf()->in_avail() > 1; cin >> office_num){
    //Print error message
    cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n";
    //Ignore any whitespace left on input stream by cin
    cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

//Office number entered is valid
valid_office_num = true;

return office_num;

Points of interest:

  1. There is always at least 1 character in cin otherwise the cin would be marked as bad and that would not be good
  2. You don't need valid_office_num if read_office_num is implemented this way, cause valid_office_num will always be set to true before returning

Upvotes: 1

ᴘᴀɴᴀʏɪᴏᴛɪs
ᴘᴀɴᴀʏɪᴏᴛɪs

Reputation: 7509

You could use a stringstream

int read_office_num()
{
    //Declaration of a local variable
    int office_num;

    string input = "";

    while (true) {
        getline(cin, input);
        stringstream myStream(input);
        if (myStream >> office_num)
            break;
        cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n" << endl;
    }

    return office_num;
}

If you want to reject input like 123 xxx you could add an additional check to verify that the received string is indeed an integer:

bool is_number(const string& s)
{
    string::const_iterator itr = s.begin();
    while (itr != s.end() && isdigit(*itr)) ++itr;
    return !s.empty() && itr == s.end();
}

int read_office_num()
{
    //Declaration of a local variable
    int office_num;

    string input = "";

    while (true) {
        getline(cin, input);
        stringstream myStream(input);
        if (is_number(input) && myStream >> office_num)
            break;
        cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n" << endl;
    }

    return office_num;
}

Upvotes: 1

Daniel Lidstr&#246;m
Daniel Lidstr&#246;m

Reputation: 10260

Here's how to do it using Boost Lexical Cast:

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

int read_office_num()
{
    using boost::lexical_cast;
    using boost::bad_lexical_cast;
    using namespace std;

    int office_num;
    while (true)
    {
        try
        {
            string input = cin.getline();
            office_num = lexical_cast<int>(*argv));
            break;
        }
        catch(const& bad_lexical_cast)
        {
            cout << "\nInvalid office number, it should only consist of digits!! Enter another:\n";
        }
    }

    return office_num;
}

Upvotes: 0

Peter
Peter

Reputation: 36597

Read a line of input as a std::string using std::getline().

Examine the string and check if it contains any characters that are not digits.

If the string only contains digits, use a std::istringstream to read an integer from the string. Otherwise report a failure, or take whatever other recovery action is needed (e.g. discard the whole string and return to read another one).

Upvotes: 1

Peter - Reinstate Monica
Peter - Reinstate Monica

Reputation: 16017

Hm. I may be missing something, but why not read a line, trim it, use regular expressions to validate a number and then exploit strstream's facilities or just atoi if you must? In all reality I'd probably just let users get away with extraneous input (but discard it if I'm sure I'm always running interactively). Following the motto "be lenient in what you accept."

The "interactive" caveat is important though. One can generally not assume that cin is a terminal. Somebody may get cocky and let your program run on a text file or in a pipeline, and then it would fail. A robust approach would separate data processing (portable) from means of input (possibly machine specific and therefore also more powerful and helpful than stdin/stdout via a console).

Upvotes: 0

Nim
Nim

Reputation: 33655

From what I gather you want the whole line to be read as a number and fail otherwise?

Well, you can use std::getline(), but you have to follow the algorithm below (I will leave the implementation to you..)

  1. use std::getline(cin, str) to read a line, and if this returns true
  2. use std::stoi(str, &pos) to convert to integer and get the position of the last integer
  3. if pos != str.size() then the whole line in not an integer (or if the above throws an exception), then it's not a valid integer, else return the value...

Upvotes: 2

Related Questions