Validating input and getline() function - c++

So I want to validate the user only enters text and not numbers. If any number is input, then I ask her again for input. I thought this would do the trick, but it doesn't seem to work:
#include <iostream>
using namespace std;
int main()
{
string name = "";
cout << "Enter name: ";
getline(cin, name);
while (!cin) // or cin.fail()
{
cout << "Numbers are not allowed, input name again: ";
cin.clear();
cin.ignore(1000, '\n'); // is this even necessary since getline() already consumes spaces and new lines?
getline(cin, name);
}
}
Because the name variable is of string type, shouldn't the cin object fail when it receives a number? How can I validate it and make sure it prompts for input again when a number is entered? Also, out of curiosity since I'm asking already, if the user enters something like: Scarlett9356, what would be a good way to re-prompt for good input? Thank you.

You could validate that there are no numbers in your string by doing this:
#include <iostream>
using namespace std;
bool validName(string name)
{
if(name.length() == 0)
return false; // `name` cannot be empty
for(int i = 0; i < name.length(); i++)
if(name[i] >= '0' && name[i] <= '9')
return false; // There is a number in `name`
return true; // `name` is valid
}
int main()
{
string name = "";
cout << "Enter name: ";
getline(cin, name);
while (!validName(name))
{
cout << "Numbers are not allowed, input name again: ";
cin.clear();
cin.ignore(1000, '\n'); // is this even necessary since getline() already consumes spaces and new lines?
getline(cin, name);
}
}

Because the name variable is of string type, shouldn't the cin object fail when it receives a number?
No. Input that consists of digits is valid as string also.
You'll need to use a different strategy for making that an invalid input.
I would suggest something along the lines of:
bool is_valid_input(std::string const& name)
{
bool is_valid = true;
// Figure out the logic for deciding when the input is not valid.
// ...
if (!is_valid )
{
cout << "Numbers are not allowed, input name again: ";
}
return is_valid;
}
int main()
{
string name = "";
do
{
cout << "Enter name: ";
getline(cin, name);
}
while ( !is_valid_input(name) );
}

If you want to limit your input to only taking in string without a number in it then you can use std::any_of and std::isdigit
std::string name = "";
std::cout << "Enter name: ";
std::getline(std::cin, name);
while (std::any_of(name.begin(), name.end(), [](char ch) { return std::isdigit(ch); }))
{
std::cout << "Numbers are not allowed, input name again: ";
std::getline(std::cin, name);
}

Related

How can I make my else statement account for anything that isn't an integer? [duplicate]

Currently having issues creating an integer validation loop in my code.
Trying to validate for an integer, however this code fails to work if you input, for example the string '22a' it sets trial_no as 22. Is there any way to check that every charachter input is indeed a string, such that '22a' or '2a2' would be considered erroneous and the loop would continue until a valid integer was input?
int trial_no;
bool valid = false;
while(!valid)
{
valid = true; //assume trial_no will be an integer
cout << "Enter the number of die throws to simulate" << endl;
cin >> trial_no;
if(cin.fail()) // exit loop condition is dependent on trail_no being a valid integer
{
cin.clear(); //corrects stream
cin.ignore(numeric_limits<streamsize>::max(), '\n'); //skips left over stream data (numeric_limit
// is in case user enters more than one letter))
cout << "Please enter an integer value" << endl;
valid = false; //cin not an integer so loop goes round to try again
}
}
Arguably the best way is to read the entire line as a string and utilize the std::stoi function:
#include <iostream>
#include <string>
int main() {
std::cout << "Enter an integer: ";
std::string tempstr;
bool valid = false;
std::getline(std::cin, tempstr);
try {
int result = std::stoi(tempstr);
std::cout << "The result is: " << result;
valid = true;
}
catch (std::invalid_argument) {
std::cout << "Could not convert to integer.";
valid = false;
}
}
As pointed out in the comments, this function can also throw a std::out_of_range exception. This assumes your compiler is C++11 (+) capable. If not, go down the std::stringstream route:
std::string tempstr;
std::getline(std::cin, tempstr);
std::stringstream ss(tempstr);
int result;
bool valid = false;
if (ss >> result) {
valid = true;
}
else {
valid = false;
}

Validate input data in an array C++

I have this program that i took it out from: https://intcpp.tech-academy.co.uk/input-validation/ and it works fine, i did some changes because i need the program to keep asking the user to enter a valid input, so that why it has the while in there however it only asks 4 times after that 4th time the input will be valid it does not matter if it right or not, Does any one know how i can fix this. Thank you
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main () {
cout << "Please enter name:" << endl;
string userName;
getline(cin, userName);
bool rejected = false;
while (rejected == false)
{
for (unsigned int i = 0; i < userName.length() && !rejected; i++)
{
if (isalpha(userName[i]))
continue;
else if (userName[i] == ' ')
continue;
else
{
cout << "Error, Please enter Patient's name again, First Name: ";
getline(cin, userName);
rejected = false;
}
}
rejected = true;
}
system("pause");
return 0;
}
Personally I would do something like
bool is_valid_username(std::string const& username)
{
// First trim the string of all leading and trailing white-space
trim(username);
if (username.length() == 0)
return false; // Input was empty or all spaces
return std::all_of(begin(username), end(username), [](char const ch)
{
return std::isalpha(ch) || ch == ' '; // Only letters and spaces are allowed
});
}
std::string get_username()
{
std::string username;
do
{
std::cout << "Please enter username: ";
std::getline(std::cin, username);
} while (!is_valid_username(username));
return username;
}
[For the trim function please see this old answer]
The get_username function will continue to ask for a username forever if the input is either empty, all spaces, or contains non-letters or not a space.
Here's a reference for std::all_of.
Here's a reference about lambda expressions.
if (isalpha(userName[i]) || (userName[i] == ' '))
continue;
else
{
cout << "Error, Please enter Patient's name again, First Name: ";
getline(cin, userName);
i = -1; //Reset check name
}
Try it!
Change unsigned int to int

C++ integer validation

Currently having issues creating an integer validation loop in my code.
Trying to validate for an integer, however this code fails to work if you input, for example the string '22a' it sets trial_no as 22. Is there any way to check that every charachter input is indeed a string, such that '22a' or '2a2' would be considered erroneous and the loop would continue until a valid integer was input?
int trial_no;
bool valid = false;
while(!valid)
{
valid = true; //assume trial_no will be an integer
cout << "Enter the number of die throws to simulate" << endl;
cin >> trial_no;
if(cin.fail()) // exit loop condition is dependent on trail_no being a valid integer
{
cin.clear(); //corrects stream
cin.ignore(numeric_limits<streamsize>::max(), '\n'); //skips left over stream data (numeric_limit
// is in case user enters more than one letter))
cout << "Please enter an integer value" << endl;
valid = false; //cin not an integer so loop goes round to try again
}
}
Arguably the best way is to read the entire line as a string and utilize the std::stoi function:
#include <iostream>
#include <string>
int main() {
std::cout << "Enter an integer: ";
std::string tempstr;
bool valid = false;
std::getline(std::cin, tempstr);
try {
int result = std::stoi(tempstr);
std::cout << "The result is: " << result;
valid = true;
}
catch (std::invalid_argument) {
std::cout << "Could not convert to integer.";
valid = false;
}
}
As pointed out in the comments, this function can also throw a std::out_of_range exception. This assumes your compiler is C++11 (+) capable. If not, go down the std::stringstream route:
std::string tempstr;
std::getline(std::cin, tempstr);
std::stringstream ss(tempstr);
int result;
bool valid = false;
if (ss >> result) {
valid = true;
}
else {
valid = false;
}

Why is a space in the string making my code loop infinitely? [duplicate]

This question already has answers here:
Infinite loop with cin when typing string while a number is expected
(4 answers)
Closed 6 years ago.
I have the following code which simply takes a string and find each character's index in the alphabet.
void encrypt()
{
string alpha = "abcdefghijklmnopqrstuvwxyz";
string word;
vector<char> temp;
char a, b;
cout << "Enter string to encrypt: \n";
cin >> word;
for (int i=0; i<word.length(); i++)
{
bool t = false;
a = word[i];
for (int j=0; j<alpha.length(); j++)
{
b = alpha[j];
if (a == b)
{
cout << a << "'s index = " << j+1 << endl;
t = true;
}
}
if (t == false)
{
cout << "space here\n";
}
}
}
when i input a word/string with no space the code works fine but when i input a string with a space the program goes into an infinite loop.
edit main() added due to request:
main()
{
int a;
bool b = false;
while (b == false)
{
cout << "1. Encrypt a string\n";
cout << "2. Decrypt a string\n";
cout << "3. Exit\n";
cout << endl;
cin >> a;
cout << endl;
if (a == 1)
{
encrypt();
}
else if (a == 2)
{
decrypt();
}
else if (a == 3)
{
b = true;
}
}
return 0;
}
cin >> word;
will read only the first word and leave the second word in the input stream. After that, the call
cin >> a;
will result in an error unless the second word starts with a number. Once the program enters a state of error, nothing is read and the program stays in a loop.
To diagnose problems like these, always check the state of the stream after a read operation.
if ( cin >> word )
{
// Use word
}
else
{
// Deal with error.
}
if ( cin >> a )
{
// Use a
}
else
{
// Deal with error.
}
To address your real problem, don't use operator>> to read space separated string. Use getline (and use a variable name different from word).
std::string str;
if ( getline(std::cin, str) )
{
// Use str
}
else
{
// Deal with error.
}
However, in order to use getline successfully, you have to make sure that after a is read, you ignore the rest of the line. Otherwise, the rest of the line will be read by getline.
if ( cin >> a )
{
// Ignore rest of the line
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Use a
}
else
{
// Deal with error.
}
Replace cin >> word; with getline(cin, word);. It will accept a line as input. Which will resolves your input containing spaces.
As far as infinite loop concern, clear the error bits on the stream cin.clear();
You can check whether cin is accepting the space separated string completely, by doing a cout instantly after the cin. If cin is not accepting the space separated string, then try using getline
Issue resolved:
Use the following:
cout << "Enter string to encrypt: ";
scanf(" %[^\n]s",word);
for (int i=0; word[i]!='\0'; i++)
{
use
include <cstdio>
Hope this solves the problem!! I will get back to you with the solution using string..

While loop fail, when reading int and string C++

I'm trying to read multiple Strings and ints from the user. I want to take pairs of Name and Age and keep doing it until the user types in "done". But my do-while crashes early on, and i can't figure out why?
int number;
string name;
do
{
cout << "Your name: " ;
getline(cin, name);
cout <<name<< " age: " ;
cin >> number;
}
while (name!="done");
Edit: Also after entering "done", i have to enter "done" also on age, why is that?
I tried to run your program in VS 2010, and even when I entered a valid number, the program would, to my surprise skip reading the next name.
It seems to me that cin >> number doesn't swallow the '\n' I naturally entered after the number.
I attempted adding a call to getchar() after each cin >> number and the program suprisingly started working as expected.
So the conclusion is, that you should clean()/ignore() after cin >> number even after the number entered was OK, or resort to using getline() (and then parsing) for reading numbers.
If you want not to input "done"'s age then you have to break out of the loop immediately after it's entered. My final code:
#include <iostream>
#include <string>
using namespace std;
int main()
{
int number;
string name;
while(true)
{
cout << "Your name: " ;
getline(cin, name);
if(name == "done")
break;
cout <<name<< " age: " ;
cin >> number;
cin.clear();
cin.ignore(std::numeric_limits<streamsize>::max(),'\n');
}
}
If someone enters an invalid age, i.e. something that isn't a number, you need to clear the flags of your cin, and also flush any remaining characters.
if( !(cin >> number )
{
cin.clear();
}
cin.ignore( std::numeric_limits<streamsize>::max() );
If it does read a number then you also need to ignore the newline after it.
Use the std namespace
This way
int number;
string name;
do
{
std::cout<<"Your name: " ;
std::cin>>name;
std::cout<<name<<" age: " ;
std::cin>>number;
}
while (name!="done");
Or this
using namspace std;
int number;
string name;
do
{
std::cout<<"Your name: " ;
std::cin>>name;
std::cout<<name<<" age: " ;
std::cin>>number;
}
while (name!="done");
#include <iostream>
#include <string>
int main ()
{
std::string name;
int age;
while (true)
{
std::cout << "Please enter your name: ";
std::cin >> name;
if ( "done" == name )
break;
std::cout << name << ", please enter your age: ";
std::cin >> age;
std::cout << name << ", you are " << age << " years old\n";
}
std::cout << "Bye.\n";
return 0;
}
Mixing use of getline() and >> can be problematic. Best to just avoid it if you can. You could also use getline() for both, and convert the int using a stringstream, or possibly atoi(), but I don't like that much.
Because you give it a string while it expect an int as age.
At the end of the first iterate, as you typed the Enter key to input the age, an extra newline was taken into the cin. So in the second iterate, the newline showed up as an empty line, causing the name to be set as an empty string. Then the program wanted an int as age. If you are not careful enough, you would input a string and cause the program crash.
One suggestion:
To replace:
getline(cin, name);
with:
do {
getline(cin, name);
} while (name.empty());