I have the program working the way I want handling only alphanumeric values but I also wish to create an exception of also allowing periods, hyphens, and underscores. However I want to negate all other characters as illegal.
void AccountData::assignAccount()
{
std::cout << "Input Account Name: ";
std::string inputAccount;
std::getline(std::cin, inputAccount);
std::string useAccount = inputAccount.substr(0, 15);
if (std::all_of(begin(useAccount), end(useAccount), std::isalnum)) varAccount = useAccount;
else
{
bool valid = true;
while (valid)
{
std::cout << "\nAccounts can only contain alphanumeric values with exceptions of _-.\n\nInput Account Name: ";
std::getline(std::cin, inputAccount);
useAccount = inputAccount.substr(0, 15);
if (std::all_of(begin(useAccount), end(useAccount), std::isalnum))
{
varAccount = useAccount;
valid = false;
}
}
}
}
You can write your own predicate and use it with all_of like this:
bool myFun(char a)
{
return (isalnum(a) || a=='_' || a=='-' || a=='.');
}
void AccountData::assignAccount()
{
std::cout << "Input Account Name: ";
std::string inputAccount;
std::getline(std::cin, inputAccount);
std::string useAccount = inputAccount.substr(0, 15);
if (std::all_of(begin(useAccount), end(useAccount), myFun)) varAccount = useAccount;
else
{
bool valid = true;
while (valid)
{
std::cout << "\nAccounts can only contain alphanumeric values with exceptions of _-.\n\nInput Account Name: ";
std::getline(std::cin, inputAccount);
useAccount = inputAccount.substr(0, 15);
if (std::all_of(begin(useAccount), end(useAccount), myFun))
{
varAccount = useAccount;
valid = false;
}
}
}
}
Related
Can someone help me with this, please? The block of code in the green box works fine, but the one in red works but not really. It's checking if the emails have both "#gmail.com" and "#yahoo.com". I want it to check both emails so that if they both contain only one "#gmail.com" or "#yahoo.com", it will exit the loop. Thank you!
#include <iostream>
using namespace std;
int main()
{
string emailOne_, emailTwo_;
bool valid = true;
do
{
cout << "Email: ";
cin >> emailOne_;
cout << "Re-enter email: ";
cin >> emailTwo_;
if (emailOne_.compare(emailTwo_) != 0)
{
valid = false;
cerr << "\t[*] ERROR: EMAILS DO NOT MATCH\n\n";
}
else
{
valid = true;
}
string myArray[] = { "#gmail.com", "#yahoo.com" };
int arrSize = sizeof(myArray) / sizeof(myArray[0]);
for (int i = 0; i < arrSize; i++)
{
auto found = emailOne_.find(myArray[i]);
if (found == string::npos)
{
valid = false;
cerr << "\t[*] ERROR: EMAILS MUST HAVE #gmail.com or #yahoo.com\n\n";
break;
}
else
{
valid = true;
}
}
} while (valid == false);
return 0;
}
You have some issues in the code
auto found = emailOne_.find(myArray[i]); will find the #gmail.com even if the entered email address is foo#gmail.com.uk, which is probably not what you want.
If the first entry in myArray doesn't match, you break out and don't test the next.
If the first entry is a match, you don't break out, so you go on and try the next string in myArray which will of course not match - and then you break out.
It's therefore currently impossible to get a valid match. By breaking out only when a valid match is found, you should be able to get the correct result.
Example with some suggested simplifications:
#include <iostream>
#include <string>
int main() {
const std::string myArray[] = {"#gmail.com", "#yahoo.com"};
std::string emailOne_;
for(;;) {
std::string emailTwo_;
std::cout << "Email: ";
std::cin >> emailOne_;
std::cout << "Re-enter email: ";
std::cin >> emailTwo_;
// simplify comparisons:
if(emailOne_ != emailTwo_) {
std::cerr << "\t[*] ERROR: EMAILS DO NOT MATCH\n\n";
continue; // back to entering email
}
bool valid = false;
// Use range-based for loops to simplify looping over arrays:
for(const std::string& end : myArray) {
// check if the end of emailOne_ is equal to `end` by comparing
// a substring of emailOne_ with the same length as end, with end:
if(emailOne_.size() > end.size() && // must be long enough
emailOne_.substr(emailOne_.size() - end.size()) == end)
{
valid = true;
break;
}
}
// check the valid state after the loop:
if(!valid) {
std::cerr << "\t[*] ERROR: EMAILS MUST HAVE #gmail.com or #yahoo.com\n";
} else {
break; // a valid email address was found.
}
}
std::cout << emailOne_ << " is a valid address\n";
}
I want to write a function that determines if all the letters of an inputted word are contained in another string of acceptable letters.
bool ContainsOnly(std::string inputtedWord, std::string acceptableLetters)
{
// ... how do I write this?
}
Here's my testing framework:
bool Tester(std::string inputtedWord, std::string acceptableLetters)
{
if (ContainsOnly(inputtedWord, acceptableLetters)) {
std::cout << "Good!" << std::endl;
return true;
}
else {
std::cout << "No good!" << std::endl;
return false;
}
}
int main()
{
std::string acceptableLetters;
std::string inputtedWord;
std::cout << "Please input the acceptable letters in your words: " << std::endl;
std::cin >> acceptableLetters;
while (inputtedWord != "STOP")
{
std::cout << "Please input the word you would like to test: (type STOP to end testing): " << std::endl;
std::cin >> inputtedWord;
Tester(inputtedWord, acceptableLetters);
}
return 0;
}
I want the following output:
Please input the acceptable letters in your words: CODING
Please input the word you would like to test: (type STOP to end testing): COIN
Good!
Please input the word you would like to test: (type STOP to end testing): COP
No good!
You can use find_first_not_of like this:
bool ContainsOnly(std::string inputtedWord, std::string acceptableLetters)
{
return inputtedWord.find_first_not_of(acceptableLetters) == std::string::npos;
}
Here's a demo.
Put all the acceptable characters to std::set.
Judge if all characters in the strings are in the set via std::all_of.
#include <set>
#include <algorithm>
bool ContainsOnly(std::string inputtedWord, std::string acceptableLetters)
{
std::set<char> okSet(acceptableLetters.begin(), acceptableLetters.end());
return std::all_of(inputtedWord.begin(), inputtedWord.end(),
[&okSet](char c)
{
return okSet.find(c) != okSet.end();
});
}
I am a student currently learning C++ and I need some help. I am currently working on a textbook exercise from the book Murach's C++ Programming. I am working on Exercise 7-2 (second exercise in chapter 7). The instructions for the program are here: Exercise 7-2 instructions I've managed to make sense of most of it, but I am currently stuck on step 9. I know how to call functions, but when I run the program, it only allows me to enter my full name. After I do that, the program ends without letting me enter my password or email. And yes I have added a return value to my variables as needed. How can I make the program let me enter my full name, password and email like it's supposed to? Nothing that I've tried seems to work. I've tried returning a value of 0, I've tried making a local variable and then adding the return value to said variable, but none of that worked. Please help me understand what I'm supposed to do as I'm still new to C++ and have a lot to learn. By the way, I'm using Microsoft Visual Studios as my IDE.
Here's the code that I have so far for my main.cpp file:
#include <iostream>
#include <string>
#include "validation.h"
using namespace std;
int main()
{
cout << "Create Account\n\n";
// get full name and parse first name
string full_name;
string first_name;
bool valid_name = false;
while (!valid_name) {
cout << "Enter full name: ";
getline(cin, full_name);
// strip whitespace from front
int i = full_name.find_first_not_of(" \n\t");
if (i > -1) {
full_name = full_name.substr(i);
}
// get first name
int space_index = full_name.find(' ');
if (space_index == -1) {
cout << "You must enter your full name. Please try again.\n";
}
else {
first_name = full_name.substr(0, space_index);
valid_name = true;
}
}
cout << endl;
bool validation::is_valid_password(string password);
bool validation::is_valid_email(string email);
// make sure first name uses initial cap
char letter = first_name[0];
first_name[0] = toupper(letter);
for (int i = 1; i < first_name.length(); ++i) {
letter = first_name[i];
first_name[i] = tolower(letter);
}
// display welcome message
cout << "Hi " << first_name << ",\n"
<< "Thanks for creating an account!\n\n";
}
And the code I have so far with the validation.cpp implementation file:
#include "validation.h"
#include <string>
#include <iostream>
using namespace std;
using namespace validation;
bool validation::is_valid_password(string password) {
bool valid_password = false;
while (!valid_password) {
valid_password = true;
cout << "Enter password: ";
getline(cin, password);
if (password.length() < 8) {
cout << "Password must be at least 8 characters.\n";
valid_password = false;
}
int index = password.find_first_of("0123456789");
if (index == -1) {
cout << "Password must include a number.\n";
valid_password = false;
}
bool special_character = false;
for (char c : password) {
if (ispunct(c)) {
special_character = true;
break;
}
}
if (!special_character) {
cout << "Password must include a special character.\n";
valid_password = false;
}
if (!valid_password) {
cout << "Please try again.\n";
}
else {
password = password.substr(0, index);
valid_password = true;
}
}
cout << endl;
return false;
}
bool validation::is_valid_email(string email) {
bool valid_email = false;
while (!valid_email) {
valid_email = true;
cout << "Enter email: ";
getline(cin, email);
int at_index = email.find('#');
if (at_index == -1) {
cout << "The email must include an at character (#).\n";
valid_email = false;
}
int dot_index = email.rfind('.');
if (dot_index == -1) {
cout << "The email must include a dot operator (.).\n";
valid_email = false;
}
bool valid_chars = true;
for (char c : email) {
if (c != '#' && c != '.' && c != '_' && c != '-') {
if (!isalnum(c)) {
valid_chars = false;
break;
}
}
}
if (at_index == 0) {
cout << "The local part of the email must include at least one character.\n";
valid_email = false;
}
if (dot_index - at_index == 1) {
cout << "The server name of the email must include at least one character.\n";
valid_email = false;
}
if (email.length() - dot_index - 1 != 3 && email.length() - dot_index - 1 != 2) {
cout << "The domain name of the email must have two or three characters.\n";
valid_email = false;
}
if (!valid_email) {
cout << "Please try again.\n";
}
else {
email = email.substr(0, at_index);
email = email.substr(0, dot_index);
valid_email = true;
}
}
cout << endl;
return false;
}
And the code I have for the validation header file:
#ifndef T_FRYE_VALIDATION_H
#define T_FRYE_VALIDATION_H
#endif // !T_FRYE_VALIDATION_H
#include <string>
using namespace std;
namespace validation {
bool is_valid_password(string password);
bool is_valid_email(string email);
}
I know that this is a lot to read, and I'm really sorry about that, but I'm at a loss as to what to do next. If anyone can help me, I'd really appreciate it.
The calls to your validation functions are malformed. You attempt to call the code
validation::isvalid_password(); from main but what you provide instead is:
bool validation::is_valid_password(string password);
bool validation::is_valid_email(string email);
which are declarations. You need to actually call the code you want.
string password;
string email;
validation::isvalid_password(password);
validation::is_valid_email(email);
What do you think these two lines do inside the main() function?
int main()
{
// ...
bool validation::is_valid_password(string password);
bool validation::is_valid_email(string email);
// ...
}
That is not a function call, that is just a declaration of the function. You should have something like:
int main()
{
// ...
std::string password;
while (!validation::is_valid_password(password));
std::string email;
while (!validation::is_valid_email(email));
// ...
}
There are many other problems in your code anyway... For example, if you are reading something inside the function is_valid_xxx (which is a bad design by itself because is_-type of functions shouldn't do anything except checking the values), you should pass the string by reference:
bool validation::is_valid_password(string &password);
Even if you don't plan to get the new value, passing something by value is a bad idea in this type of functions. Pass the value by reference to const:
bool validation::is_valid_password(const string &password);
So I am trying to use a loop in the email setter where if it isn't valid it should repeat the user input email prompt. The thing is, if I type in a valid email at first it will break the loop which is what I want but if I type in an invalid email first it will repeat the loop twice and then if I type in a valid email after that it will still repeat the loop and I'm just stuck in an infinite loop. I know I'm probably doing something stupid but I can't seem to get it working.
User.cpp
#include "pch.h"
#include "User.h"
//Email regex
std::string userEmailRegex = ".+#.+";
//Validates data against a user-defined string
bool validate(std::string regexStr, std::string data)
{
return std::regex_match(data, std::regex(regexStr));
}
User::User()
{
}
User::User(std::string email, std::string password, std::string username)
{
setEmail(email);
setPassword(password);
setUsername(username);
}
User::~User()
{
}
void User::setEmail(std::string email)
{
bool bValid = validate(userEmailRegex, email);
for (;;)
{
if (bValid)
{
this->email = email;
break;
}
else
{
std::cout << "Please enter a valid email adress!\n";
std::cout << "Email: ";
std::cin.clear();
std::cin.ignore(512, '\n');
}
}
}
Main.cpp
#include "pch.h"
#include "User.h"
#include "DkDateTime.h"
User u;
int main()
{
std::cout << "Welcome to MySocialNetwork!\n";
std::cout << "Please Login below:\n";
std::cout << std::endl;
std::vector<User> v;
std::string email;
std::cout << "Email: ";
std::cin >> email;
u.setEmail(email);
std::cout << u.getEmail() << std::endl;
std::cout << std::endl;
}
It looks like you've misplaced your validation code, and inside your setEmail code you never stored the email that the user input
void User::setEmail(std::string email)
{
for (;;)
{
bool bValid = validate(userEmailRegex, email);
if (bValid)
{
this->email = email;
break;
}
else
{
std::cout << "Please enter a valid email adress!\n";
std::cout << "Email: ";
std::cin.clear();
std::cin.ignore(512, '\n');
std::cin >> email;
}
}
}
so if you move bool bValid = validate(userEmailRegex, email); into the loop instead, you should get the expected result.
OBS: not tested but should get you toward the correct validation
You receive an email address as a parameter to a setter method, fine. But then, if it is not valid, you loop asking a new string from stdin, inside the setter method which is at least a questionable design. It will prevent to use that class in a batch context or in a service getting its input from HTTP messages or anything else not tightly bound to a terminal.
For the sake of separation of concerns, the loop should be outside of the user class: it is a UI concern, not user management. IMHO you should make validate a User class static method and move the loop into the main function:
//Validates data against a user-defined string
static bool User::validate(std::string data)
{
//Email regex
static std::string userEmailRegex = ".+#.+";
return std::regex_match(data, std::regex(User::regexStr));
}
void User::setEmail(std::string email, bool validated = false)
{
if (! (validated || validate(userEmailRegex, email))) {
raise std::invalid_argument("Invalid email");
}
this->email = email;
}
int main()
{
...
for (;;) {
std::cout << "Email: ";
std::cin >> email;
if (User::validate(email)) {
u.setEmail(email);
break;
}
else {
std::cout << "Please enter a valid email adress!\n";
}
}
std::cout << u.getEmail() << std::endl;
std::cout << std::endl;
}
I want to make a recursive function that determines if a string's characters all consist of alphabets or not. I just can't figure it out. Here's what I've done so far but it doesn't work properly.
bool isAlphabetic(string s){
const char *c = s.c_str();
if ((!isalpha(c[0]))||(!isalpha(c[s.size()])))
{
return false;
}
else if (isalpha(c[0]))
{
isAlphabetic(c+1);
return true;
}
}
can anyone suggest a correct way?
Leaving aside the many partial strings you'll create (consider passing in just the string and a starting index instead), the isalpha(c[s.size()]) check will always fail, since that's the \0 at the end of the string. You're also ignoring the result of the recursive calls.
bool isAlphabetic(string s){
if (s.size() < 1)
return true; // empty string contains no non-alphas
const char *c = s.c_str();
if (!isalpha(c[0]))
{
return false; // found a non-alpha, we're done.
}
else
{
return isAlphabetic(c+1); // good so far, try the rest of the string
}
}
Building on Paul's answer, here is a fixed implementation that won't copy any portion of the string. It accomplishes this by passing a reference to the string object and an index to the character to check; recursion simply adds 1 to this index to check the next character, and so on until the end of the string is found.
I have removed your call to c_str() since it isn't needed. string can be directly indexed.
bool isAlphabetic(string const & s, int startIndex = 0) {
// Terminating case: End of string reached. This means success.
if (startIndex == s.size()) {
return true;
}
// Failure case: Found a non-alphabetic character.
if (!isalpha(s[startIndex])) {
return false;
}
// Recursive case: This character is alphabetic, so check the rest of the string.
return isAlphabetic(s, startIndex + 1);
}
Note that the empty string is considered alphabetic by this function. You can change this by changing return true to return !s.empty().
Here a working example:
#include <iostream>
#include <string>
using namespace std;
bool isAlphabetic(string s)
{
if( s.empty() )
{
return false;
}
cout << "checking: " << s[0] << endl;
if( isalpha(s[0]) )
{
return true;
}
return isAlphabetic(&s[0]+1);
}
int main()
{
string word0 = "test";
if( isAlphabetic(word0) )
{
cout << word0 << " is alphabetic" << endl;
}
else
{
cout << word0 << " is NOT alphabetic" << endl;
}
string word1 = "1234";
if( isAlphabetic(word1) )
{
cout << word1 << " is alphabetic" << endl;
}
else
{
cout << word1 << " is NOT alphabetic" << endl;
}
string word2 = "1234w";
if( isAlphabetic(word2) )
{
cout << word2 << " is alphabetic" << endl;
}
else
{
cout << word2 << " is NOT alphabetic" << endl;
}
return 0;
}