I am stuck at where I want the user to enter a string only if the user enters a number is considered an invalid input.
Here is part of my code:
try {
cout << "What is the model of your car? ";
cin >> model;
//Error checking
// if user enter anything beside string excute this block
if (model ) {
throw runtime_error("INVALID INPUT");
}
cout << "what year is your " << model << "? ";
cin >> year;
if (cin.fail()) {
throw runtime_error("Not a number");
}
if (year < 0) {
throw runtime_error("Year can't be negative");
}
Well, model will always be a string, even if you put numbers in it. Because "1" is a string, and 65 is 'A', etc. But I think you mean how do you check if a string doesn't contain numbers.
bool contains_num(const std::string & str)
{
bool ret = false;
for(int i = 0; i < str.length(); i++)
if(isdigit(str[i]))
ret = true;
return ret;
}
This function will catch edge cases like an empty string, too.
Related
Would you be able to give me some suggestions for how I could simplify my code?
#include <iostream>
#include<fstream>
#include<string>
using namespace std;
int main() {
string current_users[5];
string new_users[5], new_user;
ifstream read;
read.open("current.txt");
for (int index = 0; index < 5; index++) {
read >> current_users[index];
}
read.close();
cout << "Enter a username: ";
cin >> new_user;
char user_choice;
int index = 0, new_index = 0;
while (index <= 5) {
if (new_user == current_users[index]) {
cout << "That username already exists."
<< " Enter a different username: ";
cin >> new_user;
index = 0;
continue;
}
if (index < 5)
index++;
else {
new_users[new_index] = new_user;
cout << "\nWelcome " << new_user << endl;
new_index++;
if (new_index < 5) {
cout << "Would you like to register another user?:"
<<"'Y' for yes or 'N' for no";
cin >> user_choice;
}
if (user_choice == 'Y' || user_choice == 'y') {
cout << "\nEnter a new username: ";
cin >> new_user;
index = 0;
}
else
break;
}
}//end of while
system("pause");
return 0;
}
This program asks a user to enter a username and checks if that username already exists. If it exists, it prompts the user to use a different username, also checking if that username already exists. If the username is unique the program welcomes the new user and asks if the user wants to register another new user (weird, but I wanted to try it). If the user wants to add another user to the "website" per say then the program runs again, checking for redundancy. I limited this program to 5 possible usernames to check and add for ease of testing. There's no errors.
The code is just chunky. I came up with this problem. I'm not in school. Can't afford it and wasn't admitted to any school where I applied. Any suggestions for online schools that offer degrees in computer science?
Here are some suggestions:
Array of Structures not parallel arrays
Use a std::vector of structures and not parallel arrays:
struct Record
{
std::string new_user;
std::string current_user;
};
std::vector<Record> database;
Processors that use a data cache like to have their elements close together. Here, new_user[0] would be next to current_user[0] in the cache.
With your parallel arrays, new_users[0] is next to current_user[4]; so the processor has to go past 4 elements to get to the first new_users element.
Loop Unrolling
You could eliminate the for loop for reading in your values:
read >> current_users[0];
read >> current_users[1];
read >> current_users[2];
read >> current_users[3];
read >> current_users[4];
This eliminates the overhead associated with a for loop.
Convert to all Lower or all Upper case before comparing
You can reduce the number of comparisons by converting to uppercase or lowercase before comparing:
if (std::toupper(user_choice) == 'Y')
Most of what you have is good. I'd wrap everything into a function and use std::find from the standard library in order to find duplicates.
template<std::size_t N, std::size_t M>
void GetUsers( std::string (&new_users)[N], std::string const (¤t_users)[M] ) {
int idx = 0;
while (idx < 5) {
std::cout << "Enter a username: ";
std::string user; std::cin >> user;
if (std::find(current_users.begin(), current_users.end(), user) != current_users.end()) {
std::cout << "That username already exists.\n";
continue;
} else {
new_users[idx++] = user;
if (idx < 5) {
std::cout << "Would you like to register another user? [Y/n]: ";
if (std::tolower(std::cin.get()) == 'y') {
continue;
}
}
break;
}
}
}
This code works fine if I enter something that isn't a number in, e.g. F: it will print the error message. However, if I enter e.g. 2F2 or , it will take the 2 and pass the check, continue in my code and on the next cin >> statement it will put the F in, and then it loops back and puts the 2 in.
How do I make it so it only accepts a single number e.g. 2 and not e.g. 2F2 or 2.2?
int bet = 0;
// User input for bet
cout << " Place your bet: ";
cin >> bet;
cout <<
// Check if the bet is a number
if (!cin.good())
{
cin.clear();
cin.ignore();
cout << endl << "Please enter a valid number" << endl;
return;
}
bool Checknum(std::string line) {
bool isnum = true;
int decimalpoint = 0;
for (unsigned int i = 0; i < line.length(); ++i) {
if (isdigit(line[i]) == false) {
if (line[i] == '.') {
++decimalpoint; // Checks if the input has a decimal point that is causing the error.
}
else {
isnum = false;
break;
}
}
}
if (decimalpoint > 1) // If it has more than one decimal point.
isnum = false;
return isnum;
}
If you take a string from the user, this should work. You can convert the string to an integer or a float(stoi or stof, respectively). It may not be the best solution there is, but this is what I have. Excuse the indentation.
Do getline to read one whole line of input from cin.
Create a stringstream to parse the string you got.
In this parser, read the number; if it fails - error
Read whitespace; if it doesn't arrive to the end of string - error
#include <sstream>
...
int bet = 0;
std::cout << " Place your bet: ";
while (true)
{
std::string temp_str;
std::getline(cin, temp_str);
std::stringstream parser(temp_str);
if (parser >> bet && (parser >> std::ws).eof())
break; // success
cout << endl << "Please enter a valid number" << endl;
}
This code keeps printing the error message until it receives valid input. Not sure this is exactly what you want, but it's pretty customary UI.
Here >> ws means "read all the whitespace". And eof ("end of file") means "end of the input string".
I have two cin validation functions in a program I'm writing - one to validate int, and the other to validate double, whilst both ensuring that the user cannot enter char values. The problem I'm having is that sometimes the function will begin validation immediately after the user is asked to enter a value, like in this case:
cout << endl << "Enter transaction ID to edit : ";
toEdit = validateIntInput(toEdit, 1, MAX_TRANS);
Or this case:
cout << "How much are you adding? : " << char(156);
tVal = validateDoubleInput(tVal, 0.01, 999999998);
However, in other cases, the program will not tell the user that their input is invalid, and simply create a new line, like in this case:
cout << "What day of the month is the bill normally paid? (1 - 31) (You can change this later) : ";
paymentDay = validateIntInput(paymentDay, 1, 31);
Or this case:
cout << "Annual interest rate (%) : ";
annualInterestRate = validateDoubleInput(annualInterestRate, 0.01, 100);
The code for validateIntInput is:
int validateIntInput(int paramToCheck, int minValue, int maxValue)
{
paramToCheck = 999999999;
string line;
while (getline(cin, line))
{
stringstream linestream(line);
linestream >> paramToCheck;
// if the first if is not included, the program will assume invalid input has been entered as soon as the user is asked for input
if (paramToCheck == 999999999)
{
cout << "";
paramToCheck = 0;
}
// if the input contains a string or is not within bounds, throw an error
else if (!linestream.eof() || paramToCheck < minValue || paramToCheck > maxValue)
{
cout << red << "Invalid input. Try again : " << white;
}
// if the input is valid, stop the loop and accept the input
else
{
break;
}
}
return paramToCheck;
}
And the code for validateDoubleInput is:
double validateDoubleInput(double paramToCheck, double minValue, double maxValue)
{
paramToCheck = 999999999;
string line;
while (getline(cin, line))
{
stringstream linestream(line);
linestream >> paramToCheck;
// if the first if is not included, the program will assume invalid input has been entered as soon as the user is asked for input
if (paramToCheck == 999999999)
{
cout << "";
paramToCheck = 0;
}
// if the input contains a string or is not within bounds, throw an error
else if (!linestream.eof() || paramToCheck < minValue || paramToCheck > maxValue)
{
cout << red << "Invalid input. Try again : " << white;
}
// if the input is valid, stop the loop and accept the input
else
{
break;
}
}
return paramToCheck;
}
NOTE: The only reason why the functions assign a value of 999999999 to the parameter and check for this on startup is because the program sometimes threw an exception even before the user had entered anything.
I really have no idea what could be going wrong here - could anyone help me get to the bottom of the problem?
Thanks in advance to anyone who can!
As I stated in the comment section, I am not getting the situation where it skips input validation. The closest I got was when I entered a char value twice, and had a minimum acceptable value of 0. In which case it would just return 0.
In the circumstance that it isn't telling you the value is invalid is because in your check you are using
if (paramToCheck == 999999999)
{
cout << ""; // Doesn't print out an error like you want it to
paramToCheck = 0;
}
I would rearrange your function to do this instead:
if (!linestream.eof() || paramToCheck < minValue ||
paramToCheck > maxValue || paramToCheck == 999999999)
{
cout << red << "Invalid input. Try again : " << white;
}
I'm currently working on a program and thinking if it is possible to implement another restrictions for the user input. The restrictions that I made as of now is that the user is only allow to input alpha and spaces, hitting enter without any input will not be also accepted.
cout<<"Input customer's lastname\t\t: ";
getline(cin,lname);
if(lname.find_first_not_of("abcdefghijklmnopqrstuvwxyz ")!=string::npos)
{
cout<<"You can only input alpha here!\n";
cin.clear();
goto p1;
}
else if(lname.empty())
{
cout<<"Please enter your lastname!\n";
cin.clear();
goto p1;
}
The another restrictions that I want is if the user input is all spaces, the program will also show a message. Second, I wonder if it's possible to detect the input if the user typed it properly like (de la Cruz) the words should be only separated by one space and if not another message will show. I cant think on how to do it, I already did some research but I couldn't found any similar to this with C++. I don't know if this is possible since I'm just starting to learn C++, or maybe I don't have enough logic at all. :(
A little help from me on checking for spaces.
bool has_only_spaces(const std::string& str)
{
return str.find_first_not_of (' ') == str.npos;
}
bool has_two_consecutive_spaces(const std::string& str)
{
for (unsigned int i = 1; i < str.length(); i++)
{
if ((str[i - 1] == str[i]) && (str[i] == ' '))
{
return true;
}
}
return false;
}
int main()
{
std::string lname;
std::cout << "Input customer's last name: ";
getline(std::cin, lname);
if (has_only_spaces(lname) || has_two_consecutive_spaces(lname))
{
std::cout << "Not a valid input" << std::endl;
std::cin.clear();
}
}
Create a function to check whether the input is valid. Use the function in a while loop.
bools isInputValid(std::string const& input)
{
// add all the checks
}
Use it as:
std::cout << "Enter input\n";
while ( getline(std::cout, line) )
{
if ( isInputValid(line) )
{
break;
}
std::cout << "Input is not vaild. Try again\n";
}
I'm making an app that requires the user to input a production order (7 digits long) like this:
int order = 0;
cout << "Insert the order number: ";
cin >> ordem;
How can I prevent the user from entering a letter? Like "I2345G789"?
Doing that, my app just enters an infinite loop. I was thinking to use a function like this:
bool isLetter(int a)
{
string s = to_string(a);
for (int i = 0; i < s.size()-1; i++)
{
if (isdigit(s[i]))
{
return false;
}
else
return true;
}
}
And then:
if (isLetter(order))
{
cout << "Insert only numbers \n";
}
But it doesn't work. Why? And how can I improve the code?
PS: I'm very new to programming, so, sorry for any beginner mistakes.
I guess you have a loop around your code in order to ask for the order number again in case it contains non-digits, for example:
while(...)
{
int order = 0;
cout << "Insert the order number: ";
cin >> order;
}
If you enter something that cannot be parsed into an integer, then the input stream will go into failure mode and that might be the reason why you end up in an infinite loop. In order to overcome your problem in a simple way, you could read a string instead:
string order;
while (true)
{
cout << "Insert the order number: ";
cin >> order;
if (isLetter(order))
cout << "Insert only numbers" << endl;
else
break;
}
The function isLetter() now takes a string and looks like this:
bool isLetter(string s)
{
// Return true if the given string contains at least one letter.
for (size_t i = 0; i < s.size(); i++)
if (!isdigit(s[i]))
return true;
// Return false if there are only digits in the given string.
return false;
}
Please note, that it should be i < s.size() and not i < s.size()-1. And maybe you should rename your function isLetter() to hasLetter(), because that would be a bit more correct.