How to validate numeric input C++

2019-01-08 00:54发布

问题:

I'd like to know how to limit an input value to signed decimals using std::cin.

回答1:

If the backing variable of the cin is a number, and the string provided is not a number, the return value is false, so you need a loop:

int someVal;

while(!(cin >> someVal)) {
   cin.reset();
   cout << "Invalid value, try again.";
}


回答2:

double i;

//Reading the value
cin >> i;

//Numeric input validation
if(!cin.eof())
{
    peeked = cin.peek();
    if(peeked == 10 && cin.good())
    {
             //Good!
             count << "i is a decimal";
        }
        else
        {
             count << "i is not a decimal";
         cin.clear();
         cin >> discard;
        }
}

This also gives an error message with the input -1a2.0 avoiding the assignation of just -1 to i.



回答3:

cin's >> operator works by reading one character at a time until it hits whitespace. That will slurp the whole string -1a2.0, which is obviously not a number so the operation fails. It looks like you actually have three fields there, -1, a, and 2.0. If you separate the data by whitespace, cin will be able to read each one without problem. Just remember to read a char for the second field.



回答4:

Combining the techniques from the top answer here and this website, I get

input.h

#include <ios>  // Provides ios_base::failure
#include <iostream>  // Provides cin

template <typename T>
T getValidatedInput()
{
    // Get input of type T
    T result;
    cin >> result;

    // Check if the failbit has been set, meaning the beginning of the input
    // was not type T. Also make sure the result is the only thing in the input
    // stream, otherwise things like 2b would be a valid int.
    if (cin.fail() || cin.get() != '\n')
    {
        // Set the error state flag back to goodbit. If you need to get the input
        // again (e.g. this is in a while loop), this is essential. Otherwise, the
        // failbit will stay set.
        cin.clear();

        // Clear the input stream using and empty while loop.
        while (cin.get() != '\n')
            ;

        // Throw an exception. Allows the caller to handle it any way you see fit
        // (exit, ask for input again, etc.)
        throw ios_base::failure("Invalid input.");
    }

    return result;
}

Usage

inputtest.cpp

#include <cstdlib>  // Provides EXIT_SUCCESS
#include <iostream>  // Provides cout, cerr, endl

#include "input.h"  // Provides getValidatedInput<T>()

int main()
{
    using namespace std;

    int input;

    while (true)
    {
        cout << "Enter an integer: ";

        try
        {
            input = getValidatedInput<int>();
        }
        catch (exception e)
        {
            cerr << e.what() << endl;
            continue;
        }

        break;
    }

    cout << "You entered: " << input << endl;

    return EXIT_SUCCESS;
}

Sample run

Enter an integer: a
Invalid input.
Enter an integer: 2b
Invalid input.
Enter an integer: 3
You entered: 3.



回答5:

I'm not trying to be rude. I just wanted to share a solution I provided which I believe is more robust and allows for better input validation.

Please refer to: My Solution to Input Validation



回答6:

I tried many techniques for reading integer input from the user using the >> operator, but in a way or another all my experiments have failed.

Now I think that getline() function (not the method with the same name on std::istream) and the strtol() function from the include cstdlib is the only predictable consistent solution for this problem. I would appreciate if someone proved me wrong. Here is something like the one I use:

#include <iostream>
#include <cstdlib>

// @arg prompt The question to ask. Will be used again on failure.
int GetInt(const char* prompt = "? ")
{
    using namespace std; // *1
    while(true)
    {
        cout << prompt;
        string s;
        getline(cin,s);
        char *endp = 0;
        int ret = strtol(s.c_str(),&endp,10);
        if(endp!=s.c_str() && !*endp)
            return ret;
    }
}
  • *1: Placing using namespace whatever; to the global scope may lead to broken "unity builds" (google!) on larger projects, so should be avoided. Practice to not use that way, even on smaller projects!
  • Reading integers from files is a very different matter. Raúl Roa's approach can be good for that if properly worked out. I also suggest that wrong input files should not be tolerated, but it really depends on the application.
  • Be warned that using >> and getline() in the same program on cin will lead to some problems. Use one of them only, or google to know how to handle the issue (not too hard).


回答7:

Something like:

double a;
cin >> a;

Should read your signed "decimal" fine.

You'll need a loop and some code to make sure it handles invalid input in a sensible way.

Good luck!