C++ input validation: how to accept only certain ints - c++

I wanted the user to input 1, 2, 3, or 4, and ONLY those numbers.
I do not want: 5, 79, 4rf, 1XXXXX, 2!, abc 1234, etc.
If I use 'cin >> ', then it would cut off the input to something like '2!' to the 2 and leave the '!' in for the next input, so getline() is preferable. The code I have listed technically works, but leaves an extra line of input when I ask again for another input after the user put in a invalid one.
Thank you for the help.
bool check = true;
string input;
int choice;
cout << "Please enter 1, 2, 3, or 4:" << endl;
getline(cin, input);
do
{
check = true;
if (input.length() != 1 || !isdigit(input[0]))
{
cout << "error, enter a valid input" << endl;
check = false;
cin.clear();
cin.ignore(INT_MAX, '\n');
getline(cin, input);
}
else
{
choice = stoi(input);
if (!(choice == 1 || choice == 2 || choice == 3 || choice == 4))
{
cout << "error, enter a valid input" << endl;
check = false;
cin.clear();
cin.ignore(INT_MAX, '\n');
getline(cin, input);
}
else
{
check = true;
}
}
} while (check == false);

getline(cin, input); consumes an entire line or fails, and failure for getline on cin is pretty much the end of cin. If you got data at all, you got the whole line. There is nothing for cin.ignore(INT_MAX, '\n'); to ignore, so the user winds up having to hit enter again before they can get to the getline(cin, input); retry.
Keeping your basic stricture, I'd clean it up to something more like
bool check = false; // assume failure
string input;
int choice;
cout << "Please enter 1, 2, 3, or 4:" << endl;
while (!check)
{
if (getline(cin, input)) // test that we got something
{
if (input.length() != 1 || !isdigit(input[0]))
{
cout << "error, enter a valid input" << endl;
// don't need to do anything else here
}
else
{
choice = input[0] - '0'; // easier conversion
if (!(choice == 1 || choice == 2 || choice == 3 || choice == 4))
{
cout << "error, enter a valid input" << endl;
// don't need to do anything else here
}
else
{
check = true; // all done
}
}
}
else
{
// to be honest there isn't much you can do here. You can clear and
// ignore, but it's hard to make unformatted input fail and still
// have a viable console. Maybe you should throw an exception, but
// for a simple program I'm all for giving up.
cerr << "Aaaaaaahhhhrrrg!\n";
exit(-1);
}
}
I'm assuming failure because I only have one place I need to set the check flag: On success! This makes it a bit easier to pick this code up and put it in a function so you can reuse it more easily. Make the loop go forever and replace check = true; with return choice;.

Related

How to Print input value

I have the following bit of code that I'm using to check whether an input is a multiple of 3, 5 or Both. If the user does not enter a number I would like it to print the value stored in UserInput. At the moment it is just returning 0, any suggestions would be much appreciated!
#include <iostream>
using namespace std;
int main()
{
int UserInput;
cout << "Please enter a number:";
cin >> UserInput;
if (!cin) {
cout << UserInput;
}
else if ((UserInput%3 == 0) && (UserInput%5 == 0)) {
cout << "FizzBuzz";
}
else if (UserInput%3 == 0) {
cout << "Fizz";
}
else if (UserInput%5 == 0) {
cout << "Buzz";
}
}
If the user input cannot be read into an int, cin is placed in a fail state and nothing is read. The contents of UserInput are useless to you. You will have to take cin out of the error state with clear and read the stream into something guaranteed to be able to hold the user's input like a std::string.
if (std::cin >> UserInput) // Get input and make sure input was read check for good input
{
// do the fizbuzz thing.
}
else
{
std::cin.clear(); // remove the error flags set by reading a non-number
std::string badinput;
getline(std::cin, badinput); // read the bad input.
std::cout << "User input: " << badinput << std::endl; // write the bad input
}

Accept only integer to input

I found this similar question being asked so many times but I still couldn't find a solution for mine.
In my case, I want to display something when the user enters a number from 1 - 5, give an error when he inputs something wrong like characters, "3g", "3.", "b3" and any float number.
I tried the code below, but it created so many other problems. Like if I enter 3g or 3.5, it'll only take the 3 and ignore the rest so the (!cin) doesn't work at all.
Second, if I input something like a character, the __userChoice will be automatically converted into 0 and the program prints out "Please select a number from 1 to 5." instead of "Invalid input, please input an integer number.\n", which is what I want.
cout << "Please select: ";
cin >> __userChoice;
if (__userChoice > 0 && __userChoice < 5) {
cout << "You select menu item " << __userChoice <<". Processing... Done!\n";
}
else if (__userChoice == 5) {
Finalization(); //call exit
}
else if (__userChoice <= 0 || __userChoice > 5) {
cout << "Please select a number from 1 to 5.\n";
}
else (!cin) {
cout << "Invalid input, please input an integer number.\n";
}
cin.clear();
cin.ignore(10000, '\n');
operator>> is not guaranteed to output a meaningful integer value if a failure occurs, but you are not checking for failure before evaluating __userChoice, and the way your ifs are structured the else (!cin) check will never be reached. But even if operator>> is successful, you are not checking if the user entered more than just an integer.
To do what you are asking for, you should read from std::cin into a std::string first using std::getline(), and then use std::istringstream or std:stoi() (or equivilent) to convert the string to an int with error checking.
For example:
bool strToInt(const std::string &s, int &value)
{
std::istringstream iss(s);
return (iss >> value) && iss.eof();
// Or:
std::size_t pos;
try {
value = std::stoi(input, &pos);
}
catch (const std::exception &) {
return false;
}
return (pos == input.size());
}
...
std::string input;
int userChoice;
std::cout << "Please select: ";
std::getline(std::cin, input);
if (strToInt(input, userChoice))
{
if (userChoice > 0 && userChoice < 5)
{
std::cout << "You selected menu item " << userChoice <<". Processing... Done!\n";
}
else if (userChoice == 5)
{
Finalization(); //call exit
}
else
{
std::cout << "Please select a number from 1 to 5.\n";
}
}
else
{
std::cout << "Invalid input, please input an integer number.\n";
}

C++ user input restriction with proper retry without "goto"

I have the following code:
qstn:
cout << "Input customer's lastname: ";
getline(cin, lname);
if (lname.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ") != string::npos) {
cout << "You can only input alpha here!\n";
cin.clear();
goto qstn;
} else if (lname.empty()) {
cout << "Please enter your firstname!\n";
cin.clear();
goto qstn;
}
int lnamel = lname.length();
int strl = str.length();
int is = 0;
for (int i = 1; i < strl;) {
i++;
is++;
if (lname[i] == lname[is] && lname[i] == ' ' || lname[0] == ' ') {
cin.clear();
cout << "Please input your lastname properly!\n";
goto qstn;
}
}
// next question here
I'm having a hard time on thinking what will be the proper logic to avoid the
goto statement, since I was college I was using it but someone here said that
it's not good to use it at all cause it might ruin my code.
I tried using the do while loop but it's not smooth as goto.
Please help!
Here's an idiom I like to use:
int i;
if (std::cin >> prompt("enter an integer: ", i))
{
std::cout << "Read user input: " << i << "\n";
} else {
std::cout << "Input failed (too many attempts). Eof? " << std::boolalpha << std::cin.eof() << "\n";
}
Here, prompt is a smart input manipulator, that takes care of handling parse errors or stream failures and retrying.
It's quite generic so actually do many things, but you don't need to indicate all the options. When the manipulator is inserted into the stream it relays to the do_manip member:
template <typename Char, typename CharT>
friend std::basic_istream<Char, CharT>& operator>>(std::basic_istream<Char, CharT>& is, checked_input<T, Prompter>& manip) {
return manip.do_manip(is);
}
The do_manip handles all the logic without any gotos :) :
std::istream& do_manip(std::istream& is) {
auto attempt = [this] { return infinite() || retries_ > 0; };
while (attempt()) {
if (!infinite())
retries_ -= 1;
prompter_(out_);
if (is >> value_) {
if (!run_validators(out_))
is.setstate(is.rdstate() | std::ios::failbit);
else
break;
} else {
out_.get() << format_error_ << "\n";
}
if (attempt()) {
is.clear();
if (flush_on_error_)
is.ignore(1024, '\n');
}
}
return is;
}
You can see that there is a possibility to have validations run before accepting the input.
Here's a somewhat full-blown demo:
Live On Coliru
int main() {
using namespace inputmagic;
int i;
if (std::cin >> prompt("enter an integer: ", i)
.retries(3)
.flush_on_error(false)
.format_error("I couldn't read that (Numbers look like 123)")
.output(std::cerr)
.validate([](int v) { return v > 3 && v < 88; }, "value not in range (3,88)")
.validate([](int v) { return 0 == v % 2; })
.validate([](int v) { return v != 42; }, "The Answer Is Forbidden")
.multiple_diagnostics())
{
std::cout << "Read user input: " << i << "\n";
} else {
std::cout << "Input failed (too many attempts). Eof? " << std::boolalpha << std::cin.eof() << "\n";
}
}
You can see it will only accept valid integers
that are >3 and <88,
that are even
except 42 (forbidden number)
When entering the numbers 21, 42 and 10 on subsequent retries, you get: live
enter an integer: 21
Value not valid
enter an integer: 42
The Answer Is Forbidden
enter an integer: 10
Read user input: 10
However, if you enter 1 all the time you get this: live
enter an integer: 1
value not in range (3,88)
Value not valid
enter an integer: 1
value not in range (3,88)
Value not valid
enter an integer: 1
value not in range (3,88)
Value not valid
Input failed (too many attempts). Eof? false
Or if you read from a single line file: live
enter an integer: value not in range (3,88)
Value not valid
enter an integer: I couldn't read that (Numbers look like 123)
enter an integer: I couldn't read that (Numbers look like 123)
Input failed (too many attempts). Eof? true
Use a function:
bool getLastName(string & lname,
string & str)
{
cout << "Input customer's lastname: ";
getline(cin, lname);
if (lname.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ")
!= string::npos)
{
cout << "You can only input alpha here!\n";
cin.clear();
return false;
}
else if (lname.empty())
{
cout << "Please enter your firstname!\n";
cin.clear();
return false;
}
int lnamel = lname.length();
int strl = str.length();
int is = 0;
for (int i = 1; i < strl;)
{
i++;
is++;
if (lname[i] == lname[is] && lname[i] == ' ' || lname[0] == ' ')
{
cin.clear();
cout << "Please input your lastname properly!\n";
return false;
}
}
return true;
}
All I've done here is replace the gotos with return false. If the program makes it to the end of the function, return true. Make the function call in a while loop:
while (!getLastName(lname, str))
{
// do nothing
}
Not only does this de-spaghettify the code, but it breaks it up into nice, small, easy to manage pieces. This is called procedural programming.
A While loop looks like your best bet. You can redo the loop with the continue keyword.
int incorrect = 0;
while(!incorrect) {
cout<<"Input customer's lastname: ";
getline(cin,lname);
if(lname.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ")!=string::npos)
{
cout<<"You can only input alpha here!\n";
cin.clear();
continue;
}
else if(lname.empty())
{
cout<<"Please enter your firstname!\n";
cin.clear();
continue;
}
int lnamel=lname.length();
int strl=str.length();
int is=0;
for(int i=1; i<strl;)
{
i++;
is++;
if(lname[i]==lname[is]&&lname[i]==' '||lname[0]==' ')
{
cin.clear();
cout<<"Please input your lastname properly!\n";
continue;
}
incorrect = 1;
}
You need to use some do while loops
for instance this:
qstn:
cout<<"Input customer's lastname: ";
getline(cin,lname);
if(lname.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ")!=string::npos)
{
cout<<"You can only input alpha here!\n";
cin.clear();
goto qstn;
}
else if(lname.empty())
{
cout<<"Please enter your firstname!\n";
cin.clear();
goto qstn;
}
could be re-written as this:
int flag;
do{
flag = 1;
cout<<"Input customer's lastname: ";
getline(cin,lname);
if(lname.find_first_not_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ")!=string::npos)
{
flag = 0;
cout<<"You can only input alpha here!\n";
}
else if(lname.empty())
{
flag = 0;
cout<<"Please enter your firstname!\n";
}
cin.clear();
} while( flag !=1 );
feel free to use a boolean type flag, it doesn't really matter
It seems to me that your code suffers from lack of clarity of purpose.
You apparently don't want the string that's entered to include a leading space, nor multiple consecutive spaces. Other than that, only alphabetic characters should be accepted.
If the user does enter multiple consecutive spaces, I'd probably just ignore all but the first. I'd probably write the code something like this:
#include <string>
#include <iostream>
#include <algorithm>
#include <cctype>
#include <sstream>
bool non_alpha(std::string const &s) {
return !std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isalpha(c) || std::isspace(c); });
}
std::string get_name(std::string const &prompt) {
std::string result;
std::string line;
do {
std::cout << prompt;
std::getline(std::cin, line);
} while (non_alpha(line));
std::istringstream words(line);
std::string word;
while (words >> word)
result += word + ' ';
return result;
}
int main() {
auto res = get_name("Please enter last name, alpha-only\n");
if (res.empty())
std::cout << "Oh well, maybe some other time";
else
std::cout << "Thanks Mr(s). " << res << "\n";
}
I'd be tempted to consider doing roughly the same for non-alphabetic characters--rather than asking the user to re-enter from the beginning, assume it's a mistake and just ignore it.

Stuck in loop when user input a letter

I'm having trouble making input validation for numeric input only. Below is my code it loops every time I input a letter, inputting the wrong number doesn't though. It loops "Invalid Input, please try again." and the only option is to close it. How can i fix this?
while (!(cin >> userOption) || userOption < 1 || userOption > 12) {
cout << "Invalid Input, please try again." << endl;
cin >> userOption;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.clear();
You don't want cin >> userOption both in the while condition and inside the loop, as that makes two inputs per loop.
The one in the while condition should be enough.
First of all initialize userOption:
unsigned int userOption = 0;
Then go like this:
while ( userOption < 1 || userOption > 12 /* or even true, like Atle suggested */)
{
cin >> userOption;
if( !cin || userOption < 1 || userOption > 12 )
{
cout << "Invalid Input, please try again." << endl;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.clear();
continue;
}
else
break; //this is looping when letter inputed
}
Try this to break out of the loop if result is OK:
while (true) {
if (!(cin >> userOption) || (userOption_tmp < 1) || (userOption_tmp > 12)) {
cout << "Invalid Input, please try again." << endl;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.clear();
}
else {
break;
}
//this is looping when letter inputed
}

while loop - nothing happens after first input

int option;
int invalidOption2 = 0;
while(invalidOption2 == 0){
try{
cin>>option;
if(!cin){
error("invalid input");
}
if(option<1 || option>2){
error("invalid input");
}
if(option==1) {
invalidOption2 = 1;
cout<<"option 1 was selected"<<endl;
}
else if(option==2) {
invalidOption2 = 1;
cout<<"option 2 was selected"<<endl;
}
}
catch(runtime_error& e) {
cerr <<"Please enter a valid option" << endl;
}
}
When using the above code, for the first value I input it will move on to the next line and wait for another input, instead of displaying either the option messages or the error message. However when I enter a second value it works as normal.
for example if I entered
a (nothing happens here, so I need to enter another value)
1
"you have selected option 1"
would be displayed and no error message for the original invalid input.
I would like to know how to get the result below instead.
a
"Please select a valid option"
1
"you have selected option 1"
any help would be appreciated
You won't get to the "Please enter a valid option" unless an
exception is thrown. You don't show error, but if it doesn't
throw an exception, you'll never get to the catch block.
Also, cin >> option almost certainly leaves a new line in the
buffer, which may cause later problems, and whien you enter an
alpha, if option is type int, cin will enter an error
state, which needs to be cleared (and the erronous input
removed). So in the error case (!cin), you need to call
cin.clear(), and in all cases, you probably want to call
cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n'
).
An alternative solution, which avoids most of these problems, is
to read complete lines (using std::getline( std::cin, line )),
and then use std::istringstream to parse the line. This will
leave std::cin in a good state, and ready to input the next
line.
Option is likely of integer type, so cin can't read 'a' into it, so it is ignored.
Is this your case?
You could modify it like this:
char option;
while(invalidOption2 == 0){
try{
cin>>option;
if(!cin){
error("invalid input");
}
if(option<'1' || option>'2'){
error("invalid input");
}
if(option=='1') {
invalidOption2 = 1;
cout<<"option 1 was selected"<<endl;
}
else if(option=='2') {
invalidOption2 = 1;
cout<<"option 2 was selected"<<endl;
}
}
catch(runtime_error& e) {
cerr <<"Please enter a valid option" << endl;
}
}
This should work how you expect it to.
However, construction like this:
char option;
while (cin >> option) {
if ((option == '1') || (option == '2')) {
break;
}
else {
cout << "Please enter 1 or 2." << endl;
}
}
cout << "option " << option " << " was selected" << endl;
Is probably better ^_^
I think this looks a better code (at least for the eyes):
int option;
int invalidOption2 = 0;
while(invalidOption2 == 0)
{
try
{
cin>>option;
switch(option)
{
case 1 :
invalidOption2 = 1;
cout<<"option 1 was selected"<<endl;
break;
case 2 :
invalidOption2 = 1;
cout<<"option 2 was selected"<<endl;
break;
default :
error("invalid input");
}
catch(runtime_error& e)
{
cerr <<"Please enter a valid option" << endl;
}
}
If you still think you have problems with int and char with cin then better use :
option = getchar();
I think calling cin twice may be the issue here, but I'm not certain. Something like this should work well too:
char option[80]; // 80 is the standard terminal line length
bool loop = 1;
while(loop)
{
cin >> option;
if (option[1]=='\n') // testing if string is length 1
{
cout << "Please select a valid input\n";
continue; // if not, display error and try again
};
switch(option[0])
{
case '1': loop=0; break; // add whatever before break
case '2': loop=0; break;
default: cout << "please select a valid input\n"; break;
};
};
cout << "Option "<< option << "was selected\n";
I didn't compile this, but it should be okay.