What is the best way in C++11 (ie. using C++11 techniques) to validate cin input? I've read lots of other answers (all involving cin.ignore, cin.clear, etc.), but those methods seem clumsy and result in lots of duplicated code.
Edit: By 'validation', I mean that both well-formed input was provided, and that it satisfies some context-specific predicate.
I'm posting my attempt at a solution as an answer in the hopes that it is useful to somebody else. It is not necessary to specify a predicate, in which case the function will check only for well-formed input. I am, of course, open to suggestions.
//Could use boost's lexical_cast, but that throws an exception on error,
//rather than taking a reference and returning false.
template<class T>
bool lexical_cast(T& result, const std::string &str) {
std::stringstream s(str);
return (s >> result && s.rdbuf()->in_avail() == 0);
}
template<class T, class U>
T promptValidated(const std::string &message, std::function<bool(U)> condition = [](...) { return true; })
{
T input;
std::string buf;
while (!(std::cout << message, std::getline(std::cin, buf) && lexical_cast<T>(input, buf) && condition(input))) {
if(std::cin.eof())
throw std::runtime_error("End of file reached!");
}
return input;
}
Here's an example of its usage:
int main(int argc, char *argv[])
{
double num = promptValidated<double, double>("Enter any number: ");
cout << "The number is " << num << endl << endl;
int odd = promptValidated<int, int>("Enter an odd number: ", [](int i) { return i % 2 == 1; });
cout << "The odd number is " << odd << endl << endl;
return 0;
}
If there's a better approach, I'm open to suggestions!
If by validation you mean that when you want an int you want to know if an int was really entered, then just put the input in an if condition:
int num;
while (!(std::cin >> num))
{
std::cout << "Whatever you entered, it wasn't an integer\n";
}
std::cout << "You entered the integer " << num << '\n';
Related
Please note that I am a complete beginner at C++. I'm trying to write a simple program for an ATM and I have to account for all errors. User may use only integers for input so I need to check if input value is indeed an integer, and my program (this one is shortened) works for the most part.
The problem arises when I try to input a string value instead of an integer while choosing an operation. It works with invalid value integers, but with strings it creates an infinite loop until it eventually stops (unless I add system("cls"), then it doesn't even stop), when it should output the same result as it does for invalid integers:
Invalid choice of operation.
Please select an operation:
1 - Balance inquiry
7 - Return card
Enter your choice and press return:
Here is my code:
#include <iostream>
#include <string>
using namespace std;
bool isNumber(string s) //function to determine if input value is int
{
for (int i = 0; i < s.length(); i++)
if (isdigit(s[i]) == false)
return false;
return true;
}
int ReturnCard() //function to determine whether to continue running or end program
{
string rtrn;
cout << "\nDo you wish to continue? \n1 - Yes \n2 - No, return card" << endl;
cin >> rtrn;
if (rtrn == "1" and isNumber(rtrn)) { return false; }
else if (rtrn == "2" and isNumber(rtrn)) { return true; }
else {cout << "Invalid choice." << endl; ReturnCard(); };
return 0;
}
int menu() //function for operation choice and execution
{
int choice;
do
{
cout << "\nPlease select an operation:\n" << endl
<< " 1 - Balance inquiry\n"
<< " 7 - Return card\n"
<< "\nEnter your choice and press return: ";
int balance = 512;
cin >> choice;
if (choice == 1 and isNumber(to_string(choice))) { cout << "Your balance is $" << balance; "\n\n"; }
else if (choice == 7 and isNumber(to_string(choice))) { cout << "Please wait...\nHave a good day." << endl; return 0; }
else { cout << "Invalid choice of operation."; menu(); }
} while (ReturnCard()==false);
cout << "Please wait...\nHave a good day." << endl;
return 0;
}
int main()
{
string choice;
cout << "Insert debit card to get started." << endl;
menu();
return 0;
}
I've tried every possible solution I know, but nothing seems to work.
***There is a different bug, which is that when I get to the "Do you wish to continue?" part and input any invalid value and follow it up with 2 (which is supposed to end the program) after it asks again, it outputs the result for 1 (continue running - menu etc.). I have already emailed my teacher about this and this is not my main question, but I would appreciate any help.
Thank you!
There are a few things mixed up in your code. Always try to compile your code with maximum warnings turned on, e.g., for GCC add at least the -Wall flag.
Then your compiler would warn you of some of the mistakes you made.
First, it seems like you are confusing string choice and int choice. Two different variables in different scopes. The string one is unused and completely redundant. You can delete it and nothing will change.
In menu, you say cin >> choice;, where choice is of type int. The stream operator >> works like this: It will try to read as many characters as it can, such that the characters match the requested type. So this will only read ints.
Then you convert your valid int into a string and call isNumber() - which will alway return true.
So if you wish to read any line of text and handle it, you can use getline():
string inp;
std::getline(std::cin, inp);
if (!isNumber(inp)) {
std::cout << "ERROR\n";
return 1;
}
int choice = std::stoi(inp); // May throw an exception if invalid range
See stoi
Your isNumber() implementation could look like this:
#include <algorithm>
bool is_number(const string &inp) {
return std::all_of(inp.cbegin(), inp.cend(),
[](unsigned char c){ return std::isdigit(c); });
}
If you are into that functional style, like I am ;)
EDIT:
Btw., another bug which the compiler warns about: cout << "Your balance is $" << balance; "\n\n"; - the newlines are separated by ;, so it's a new statement and this does nothing. You probably wanted the << operator instead.
Recursive call bug:
In { cout << "Invalid choice of operation."; menu(); } and same for ReturnCard(), the function calls itself (recursion).
This is not at all what you want! This will start the function over, but once that call has ended, you continue where that call happened.
What you want in menu() is to start the loop over. You can do that with the continue keyword.
You want the same for ReturnCard(). But you need a loop there.
And now, that I read that code, you don't even need to convert the input to an integer. All you do is compare it. So you can simply do:
string inp;
std::getline(std::cin, inp);
if (inp == "1" || inp == "2") {
// good
} else {
// Invalid
}
Unless that is part of your task.
It is always good to save console input in a string variable instead of another
type, e.g. int or double. This avoids trouble with input errors, e.g. if
characters instead of numbers are given by the program user. Afterwards the
string variable could by analyzed for further actions.
Therefore I changed the type of choice from int to string and adopted the
downstream code to it.
Please try the following program and consider my adaptations which are
written as comments starting with tag //CKE:. Thanks.
#include <iostream>
#include <string>
using namespace std;
bool isNumber(const string& s) //function to determine if input value is int
{
for (size_t i = 0; i < s.length(); i++) //CKE: keep same variable type, e.g. unsigned
if (isdigit(s[i]) == false)
return false;
return true;
}
bool ReturnCard() //function to determine whether to continue running or end program
{
string rtrn;
cout << "\nDo you wish to continue? \n1 - Yes \n2 - No, return card" << endl;
cin >> rtrn;
if (rtrn == "1" and isNumber(rtrn)) { return false; }
if (rtrn == "2" and isNumber(rtrn)) { return true; } //CKE: remove redundant else
cout << "Invalid choice." << endl; ReturnCard(); //CKE: remove redundant else + semicolon
return false;
}
int menu() //function for operation choice and execution
{
string choice; //CKE: change variable type here from int to string
do
{
cout << "\nPlease select an operation:\n" << endl
<< " 1 - Balance inquiry\n"
<< " 7 - Return card\n"
<< "\nEnter your choice and press return: ";
int balance = 512;
cin >> choice;
if (choice == "1" and isNumber(choice)) { cout << "Your balance is $" << balance << "\n\n"; } //CKE: semicolon replaced by output stream operator
else if (choice == "7" and isNumber(choice)) { cout << "Please wait...\nHave a good day." << endl; return 0; }
else { cout << "Invalid choice of operation."; } //CKE: remove recursion here as it isn't required
} while (!ReturnCard()); //CKE: negate result of ReturnCard function
cout << "Please wait...\nHave a good day." << endl;
return 0;
}
int main()
{
string choice;
cout << "Insert debit card to get started." << endl;
menu();
return 0;
}
I'm new to statically typed C++. In JavaScript, I could just check the data type first, but that seems to be very complicated, and the answers all seem to imply that you aren't "getting" the language.
here's the code I was testing out rand() with, where I came upon the issue of converting strings to integers:
int main(){
std::string input;
cout <<endl<< "What to do?"<<endl;
cin >> input;
if (input == "rand")
{
cout << "what is the max?" << endl;
cin >> input;
int number;
if (stoi(input) > 1) {
number = stoi(input);
}
else {
number = 10;
cout << "using 10"<<endl;
}
cout << rand() % stoi(input);
return main();
}
}
so in Javascript, I would just check the type of input or result, but what do people do in C++?
Not allowed to say thank you in the comments so I'm saying thank you here!
Well, let's try out what happens: https://godbolt.org/z/1zahbW
As you can see, std::stoi throws an exception if you pass it invalid input or its input is out of range.
You should, however, be aware that std::cin >> some_string; is somewhat non-obvious in that it reads in the first "word", not a line or anything like that, and that std::stoi does the same thing (again).
One way to perform the check, could be like this:
#include <string>
#include <iostream>
int main(){
std::cout << "Please give me a number: " << std::flush;
std::string input;
std::getline(std::cin, input);
try {
auto value = std::stoi(input);
std::cout << "Thanks for the " << value << " (but the string was \"" << input << "\")\n";
} catch(std::invalid_argument const&) {
std::cout << "The provided value is not an integer\n";
} catch(std::out_of_range const&) {
std::cout << "The provided value is out of range\n";
}
}
https://godbolt.org/z/rKrv8G
Note that this will parse " 42 xyz" as 42. If that is a problem for your use case, you may wish to use std::strtoi directly, or to check if your input is valid before parsing (e.g., using a regex)
Regarding to the documentation of std::stoi it throws an std::invalid_argument.
What you could do is to place your std::stoi call inside a try and then catch the std::invalid_argument, but personally i wouldn't do that.
Instead, it is (most likely) a lot better to check if the first character of your input is an int, because if it is one, it can simply be parsed by std::stoi.
You can do that by e.g. doing the following:
int max = 0;
std::string input;
std::cin >> input;
if(std::isdigit(input[0]))
max = std::stoi(input);
EDIT: Please note that this would not respect the case of a too big number, to handle that case you would need an additional check.
As I am very new to exceptions, I am having some trouble with the below memory exception code. Basically, I would need to calculate and show sum of 10 positive inputs. During the input, my program should use exception mechanism to display a message that it doesn't allow negative numbers / not-a-numbers (NaN) if so is the input and then exit the program. I am told to use std::cin.fail() to detect whether the value entered fits the variable type(but I am not sure how to implement it). Would appreciate your help thanks!
#include <iostream>
int main() {
int number;
int a=-2;
try {
for(int i=0; i<10;i++) {
std::cin>>number;
}
}
catch (...) {
number==-number?
std::cout << "Its negative"<<std::endl;
number==a?
std::cout << "Its NaN"<<std::endl;
}
}
You say "10 positive inputs", but you have int number;. If your number is going to be an integer, then it can't take fractions like 33.44 for instance, and it also can't be NaN (so you wouldn't need to check for that). If you want to allow fractions, do double number instead. That aside, the check could look like this:
for (int i = 0; i < 10; i++) {
std::cin >> number;
if (std::cin.fail()) {
std::cin.clear();
std::string input;
std::cin >> input;
std::cout << "input failed! Invalid input: " << input << std::endl;
return -1;
}
After every inputted number, you do the std::cin.fail() check. If it failed, you can just return. In this example, I also made it print the invalid input. For that you need to call std::cin.clear(); to reset that error flag, and then you can put that invalid input into an std::string (which should work, unlike with the number). You need to include <string> for that. If you don't need to print anything, you can just return and forget about the clearing, the string and the output. Also, in this example, since the mess made by the invalid input is neatly cleaned up, you could take more (valid) inputs again if you don't return instead.
Next, checking for negative numbers:
if (number < 0) {
std::cout << "Error: Negative number!" << std::endl;
return -1;
}
Finally, the NaN check (for double):
if (isnan(number)) {
std::cout << "Error: number is NaN!" << std::endl;
return -1;
}
You need to include <math.h> for the isnan.
Putting it all together, it could look like this:
#include <iostream>
#include <string> // std::istream >> std::string
#include <math.h> // isnan
int main() {
double number;
for (int i = 0; i < 10; i++) {
std::cin >> number;
if (std::cin.fail()) {
std::cin.clear();
std::string input;
std::cin >> input;
std::cout << "input failed! Invalid input: " << input << std::endl;
return -1;
}
if (number < 0) {
std::cout << "Error: Negative number!" << std::endl;
return -1;
}
if (isnan(number)) {
std::cout << "Error: number is NaN!" << std::endl;
return -1;
}
}
return 0;
}
I am wrote a function that can replace cin for integers and potentially doubles, that includes error checking capabilities. Using cin.fail() I was able to check for most cases, but that didn't cover the case where the input was followed by a string without a space. For example, "23tewnty-three." The following code accommodates this.
int getUserInt(string prompt = "Enter an integer: ", string errorMessage "Error: Invalid Input") {
const int IGNORE_MAX = 100;
int userInt = 0;
bool isContinue = true;
do {
// initialize and reset variables
string inputStr;
istringstream inputCheck;
userInt = 0;
// get input
cout << prompt;
cin >> inputStr;
inputCheck.str(inputStr);
// check for valid input
inputCheck >> userInt;
if (!inputCheck.fail()) {
// check for remaining characters
if (inputCheck.eof()) { // Edit: This is the section that I tried replacing with different code (made code compilable in response to comment)
isContinue = false;
}
else {
cout << errorMessage << endl;
}
}
else {
// reset cin and print error message
cin.ignore(IGNORE_MAX, '\n');
cin.clear();
cout << errorMessage << endl;
}
} while (isContinue);
return userInt;
}
This code works, but the reason I am posting this to Stack Overflow instead of Code Review is because my main question is about why some of code didn't work as I expected. The following is what I tried in place of inputCheck.eof() in the previous code. My questions are what are the differences between the following code? Why didn't methods 2) and 3) work? and which method is preferred?
inputCheck.eof()
inputCheck.peek() == EOF
inputCheck.str().empty()
inputCheck.rdbuf()->in_avail() == 0
1) and 4) worked as expected, but 2) and 3) did not.
Edit:
I believe 3) didn't work as expected because inputCheck.str() returns what was contained in inputStr when inputCheck.str(inputStr) was called. However, I have no idea why inputCheck.peek() == EOF didn't work.
If this is relevant information, I am compiling and running on windows through bash g++.
For every prompt you provide, you can expect your user to press Enter. Obtain input as a string, then try to convert. (Don’t try to convert from cin.)
Bonus: here’s a function to perform conversion.
template <typename T>
auto string_to( const std::string & s )
{
T value;
std::istringstream ss( s );
return ((ss >> value) and (ss >> std::ws).eof())
? value
: std::optional<T> { };
}
You’ll need C++17 for that, or to #include <boost/optional.hpp> instead.
Now:
std::cout << "Enter an integer! ";
std::string s;
getline( std::cin, s );
auto x = string_to <int> ( s );
if (!x)
{
std::cout << "That was _not_ an integer.\n";
}
else
{
std::cout << "Good job. You entered the integer " << *x << ".\n";
}
No more worrying about clearing or resetting cin. Handily perform some loops (such as allow user three attempts before quitting). Et cetera.
I was typing this and it asks the user to input two integers which will then become variables. From there it will carry out simple operations.
How do I get the computer to check if what is entered is an integer or not? And if not, ask the user to type an integer in. For example: if someone inputs "a" instead of 2, then it will tell them to reenter a number.
Thanks
#include <iostream>
using namespace std;
int main ()
{
int firstvariable;
int secondvariable;
float float1;
float float2;
cout << "Please enter two integers and then press Enter:" << endl;
cin >> firstvariable;
cin >> secondvariable;
cout << "Time for some simple mathematical operations:\n" << endl;
cout << "The sum:\n " << firstvariable << "+" << secondvariable
<<"="<< firstvariable + secondvariable << "\n " << endl;
}
You can check like this:
int x;
cin >> x;
if (cin.fail()) {
//Not an int.
}
Furthermore, you can continue to get input until you get an int via:
#include <iostream>
int main() {
int x;
std::cin >> x;
while(std::cin.fail()) {
std::cout << "Error" << std::endl;
std::cin.clear();
std::cin.ignore(256,'\n');
std::cin >> x;
}
std::cout << x << std::endl;
return 0;
}
EDIT: To address the comment below regarding input like 10abc, one could modify the loop to accept a string as an input. Then check the string for any character not a number and handle that situation accordingly. One needs not clear/ignore the input stream in that situation. Verifying the string is just numbers, convert the string back to an integer. I mean, this was just off the cuff. There might be a better way. This won't work if you're accepting floats/doubles (would have to add '.' in the search string).
#include <iostream>
#include <string>
int main() {
std::string theInput;
int inputAsInt;
std::getline(std::cin, theInput);
while(std::cin.fail() || std::cin.eof() || theInput.find_first_not_of("0123456789") != std::string::npos) {
std::cout << "Error" << std::endl;
if( theInput.find_first_not_of("0123456789") == std::string::npos) {
std::cin.clear();
std::cin.ignore(256,'\n');
}
std::getline(std::cin, theInput);
}
std::string::size_type st;
inputAsInt = std::stoi(theInput,&st);
std::cout << inputAsInt << std::endl;
return 0;
}
Heh, this is an old question that could use a better answer.
User input should be obtained as a string and then attempt-converted to the data type you desire. Conveniently, this also allows you to answer questions like “what type of data is my input?”
Here is a function I use a lot. Other options exist, such as in Boost, but the basic premise is the same: attempt to perform the string→type conversion and observe the success or failure:
template <typename T>
auto string_to( const std::string & s )
{
T value;
std::istringstream ss( s );
return ((ss >> value) and (ss >> std::ws).eof()) // attempt the conversion
? value // success
: std::optional<T> { }; // failure
}
Using the optional type is just one way. You could also throw an exception or return a default value on failure. Whatever works for your situation.
Here is an example of using it:
int n;
std::cout << "n? ";
{
std::string s;
getline( std::cin, s );
auto x = string_to <int> ( s );
if (!x) return complain();
n = *x;
}
std::cout << "Multiply that by seven to get " << (7 * n) << ".\n";
limitations and type identification
In order for this to work, of course, there must exist a method to unambiguously extract your data type from a stream. This is the natural order of things in C++ — that is, business as usual. So no surprises here.
The next caveat is that some types subsume others. For example, if you are trying to distinguish between int and double, check for int first, since anything that converts to an int is also a double.
There is a function in c called isdigit(). That will suit you just fine. Example:
int var1 = 'h';
int var2 = '2';
if( isdigit(var1) )
{
printf("var1 = |%c| is a digit\n", var1 );
}
else
{
printf("var1 = |%c| is not a digit\n", var1 );
}
if( isdigit(var2) )
{
printf("var2 = |%c| is a digit\n", var2 );
}
else
{
printf("var2 = |%c| is not a digit\n", var2 );
}
From here
If istream fails to insert, it will set the fail bit.
int i = 0;
std::cin >> i; // type a and press enter
if (std::cin.fail())
{
std::cout << "I failed, try again ..." << std::endl
std::cin.clear(); // reset the failed state
}
You can set this up in a do-while loop to get the correct type (int in this case) propertly inserted.
For more information: http://augustcouncil.com/~tgibson/tutorial/iotips.html#directly
You can use the variables name itself to check if a value is an integer.
for example:
#include <iostream>
using namespace std;
int main (){
int firstvariable;
int secondvariable;
float float1;
float float2;
cout << "Please enter two integers and then press Enter:" << endl;
cin >> firstvariable;
cin >> secondvariable;
if(firstvariable && secondvariable){
cout << "Time for some simple mathematical operations:\n" << endl;
cout << "The sum:\n " << firstvariable << "+" << secondvariable
<<"="<< firstvariable + secondvariable << "\n " << endl;
}else{
cout << "\n[ERROR\tINVALID INPUT]\n";
return 1;
}
return 0;
}
I prefer to use <limits> to check for an int until it is passed.
#include <iostream>
#include <limits> //std::numeric_limits
using std::cout, std::endl, std::cin;
int main() {
int num;
while(!(cin >> num)){ //check the Input format for integer the right way
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
cout << "Invalid input. Reenter the number: ";
};
cout << "output= " << num << endl;
return 0;
}
Under C++11 and later, I have the found the std::stoi function very useful for this task. stoi throws an invalid_argument exception if conversion cannot be performed. This can be caught and handled as shown in the demo function 'getIntegerValue' below.
The stoi function has a second parameter 'idx' that indicates the position of the first character in the string after the number. We can use the value in idx to check against the string length and ascertain if there are any characters in the input other than the number. This helps eliminate input like 10abc or a decimal value.
The only case where this approach fails is when there is trailing white space after the number in the input, that is, the user enters a lot of spaces after inputting the number. To handle such a case, you could rtrim the input string as described in this post.
#include <iostream>
#include <string>
bool getIntegerValue(int &value);
int main(){
int value{};
bool valid{};
while(!valid){
std::cout << "Enter integer value: ";
valid = getIntegerValue(value);
if (!valid)
std::cout << "Invalid integer value! Please try again.\n" << std::endl;
}
std::cout << "You entered: " << value << std::endl;
return 0;
}
// Returns true if integer is read from standard input
bool getIntegerValue(int &value){
bool isInputValid{};
int valueFromString{};
size_t index{};
std::string userInput;
std::getline(std::cin, userInput);
try {
//stoi throws an invalid_argument exception if userInput cannot be
//converted to an integer.
valueFromString = std::stoi(userInput, &index);
//index being different than length of string implies additional
//characters in input that could not be converted to an integer.
//This is to handle inputs like 10str or decimal values like 10.12
if(index == userInput.length()) isInputValid = true;
}
catch (const std::invalid_argument &arg) {
; //you could show an invalid argument message here.
}
if (isInputValid) value = valueFromString;
return isInputValid;
}
You could use :
int a = 12;
if (a>0 || a<0){
cout << "Your text"<<endl;
}
I'm pretty sure it works.