Modifying the Create Account Program - c++

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);

Related

Comparing strings in this instances work differently, any reason?

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";
}

operator _surrogate_func: no matching overload found and 2 other errors

I've written a code which creates a list of buses that you can modify and manage. The whole managing process is proceed by writing strings in console. After running code I'm receiving 3 errors, none of which I understand therefore can fix. Code is planned this way:
NEW_BUS - Adds new bus in list by taking it's number, amount of stops and stop list.
ALL_BUSES - Display all buses in lexicographical order (by their name)
STOPS_FOR_BUS - Display all stops specific bus follows.
BUSES_FOR_STOP - Diplay all buses that go though the specific stop.
Here are error list:
1. operator _surrogate_func: no matching overload found
2. Failed to specialize function template 'unknown-type std::less::operator ()(_Ty 1 &&,_Ty2 &&)
3. illegal expression
All errors come from line 617 from xutility file.
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <tuple>
#include <iterator>
using namespace std;
class Bus {
public:
int StopsAmount;
string BusNumber;
vector<string> Stops;
Bus(tuple<string, int, vector<string>> BusParams) {
BusNumber = get<0>(BusParams);
StopsAmount = get<1>(BusParams);
Stops = get<2>(BusParams);
}
void ShowStops() {
cout << BusNumber << ": ";
for (int i = 0; i < StopsAmount; i++) cout << Stops[i] << " ";
cout << "\n";
}
bool FindStop(string Stop) {
for (int i = 0; i < StopsAmount; i++) {
if (Stops[i] == Stop) {
return true;
}
}
return false;
}
};
class BusTraffic {
public:
BusTraffic() {
while (true) {
string Request;
cin >> Request;
switch (Request[0]) {
case 'N': NEW_BUS(Request.substr(8, Request.length() - 8)) ;
break;
case 'B': BUSES_FOR_STOP(Request.substr(15, Request.length() - 15));
break;
case 'S': STOPS_FOR_BUS(Request.substr(14, Request.length() - 14));
break;
case 'A': ALL_BUSES();
break;
}
}
}
private:
list<Bus> BusList;
void NEW_BUS(string Request) {
BusList.push_back(Bus::Bus(SplitString(Request)));
}
void BUSES_FOR_STOP(string Stop) {
cout << Stop << ": ";
for (list<Bus>::iterator It = BusList.begin(); It != BusList.end(); It++) {
if (It->FindStop(Stop)) {
cout << It->BusNumber << " ";
}
}
cout << endl;
}
void STOPS_FOR_BUS(string Name) {
cout << Name << ": ";
for (list<Bus>::iterator It = BusList.begin(); It != BusList.end(); It++) {
if (It->BusNumber == Name) {
It->ShowStops();
}
}
}
void ALL_BUSES() {
if (BusList.size() > 0) {
BusList.sort();
for (list<Bus>::iterator It = BusList.begin(); It != BusList.end(); It++) {
cout << It->BusNumber << ": ";
It->ShowStops();
}
}
else {
cout << "No buses" << endl;
}
}
// Converting string to information about bus
tuple<string, int, vector<string>> &SplitString(string str) {
tuple<string, int, vector<string>> BusParams;
string Word = "";
int WordNum = 0;
for (auto Letter : str) {
if (Letter == ' ') {
if (WordNum == 0) get<0>(BusParams) = Word;
if (WordNum == 1) get<1>(BusParams) = stoi(Word);
if (WordNum == 2) get<2>(BusParams).push_back(Word);
Word = "";
WordNum++;
}
else {
Word = Word + Letter;
}
}
get<2>(BusParams).push_back(Word);
return BusParams;
}
};
int main() {
BusTraffic TestTraffic;
return 0;
}
Unfortunately, you are absolutely correct. the error message here is just terrible. It's just from experience that you learn to interpret that as a missing less-than-operator.
So something in the lines of:
bool operator< (const Bus& lhs, const Bus& rhs) {
// however you want to sort them...
return (lhs.BusNumber < rhs.BusNumber);
}
The other small bug is within NEW_BUS: In C++ you don't need to specify the constructor name. So it's not Bus::Bus it's just Bus
Last but not least your SplitString is returning a reference to a local variable. In general that's a bad idea as that memory might simply not be accessible anymore when you try to. Just remove "&" from the return type. Further explanations.
You're trying to sort a list of buses. By the sounds of it there is no overload for the less than operator. This means C++ doesn't know how to compare objects of type bus. Add this to your Bus class and it should work. This overload of the less than operator returns true if the other bus is less than the current bus.
bool operator < (const Bus& otherBus) const {
if(otherBus.StopsAmount < this.StopsAmount) {
return true;
}
return false;
}
Useful links with more details:
http://fusharblog.com/3-ways-to-define-comparison-functions-in-cpp/
https://www.tutorialspoint.com/cplusplus/relational_operators_overloading.htm

How to repeat a user input email if not valid

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;
}

how to add characters that aren't alphanumeric

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;
}
}
}
}

designing three book classes for c++

I'm very new to the program world and I am stuck at the moment. I'm suppose to design a bookrecord that contains the title, author, publisher, category (Dewey decimal), and date of publication. For booklist I need to know how to add a new bookrecord object, find a bookrecord given the title, remove a bookrecord object, and show all the bookrecord objects. For booklistUI, I need the menu, command processor, add records, remove records, display all records. I am sort of lost as to where I should begin how would I start it out? I do have some of it so far... is it correct?
This is what I have so far
/*
Title: CSU Bodie.cpp
Description: implements three classes to make a simple searchable list of the library holdings.
*/
#include <iostream>;
#include <string>;
#include <vector>;
using namespace std;
//Define a name class...
class ShortName {
string FirstName;
string LastName;
public:
ShortName(); //Define a default constructor...
ShortName(string, string); //...and an initializing one...
//Define some mutators...
void SetFirst(string First) { FirstName = First; }
void SetLast(string Last) { LastName = Last; }
//...and some accessors...
string GetFirst() { return FirstName; }
string GetLast() { return LastName; }
};
ShortName::ShortName()
{
FirstName = "";
LastName = "";
}
ShortName::ShortName(string First, string Last)
{
FirstName = First;
LastName = Last;
}
//Define a list class...
class ShortNameList {
vector<ShortName> List; //Create a specific sized list...
public:
ShortNameList() {;} //Nothing for it to do at this point...
void AddRecord(string, string);
void ShowList();
};
void ShortNameList::AddRecord( string First, string Last)
{
ShortName NameBuffer;
NameBuffer.SetFirst(First);
NameBuffer.SetLast(Last);
List.push_back(NameBuffer);
}
void ShortNameList::ShowList()
{
int K;
for(K = 0 ; K < List.size() ; K++)
cout << List[K].GetFirst() << " " List[K].GetLast() << endl;
}
class ShortNameUI {
ShortNameList Collection;
public:
ShortNameUI() {;}
void Menu();
void AddNewRecord();
void RunIT();
};
//This gives us options...
void ShortNameUI::Menu()
{
cout << "Choices..." << endl;
cout << "====================================================================" << endl;
cout << "\\tAdd.........allows the user to enter names." << endl;
cout << "\\tDisplay......shows the names." << endl;
cout << "\\tQuit.........terminates the program" << endl;
cout << "====================================================================" << endl;
}
void ShortNameUI::RunIT()
{
string Command;
while(true) {
Menu();
cout << "Command: ";
getline(cin, Command);
if(Command == "Quit")
break;
else if(Command == "Add")
AddNewRecord();
else if(Command == "Display")
Collection.ShowList();
}
}
void ShortNameUI::AddNewRecord()
{
string First, Last;
cout << "Enter Names Below, Stop To Quit" << endl;
while(true) {
cout << "First Name: ";
getline(cin, First);
if(First == "Stop")
break;
cout << "Last Name: ";
getline(cin, Last);
if(Last == "Stop")
break;
Collection.AddRecord(First, Last);
}
}
int main()
{
ShortNameUI NameList;
NameList.RunIT();
}
}
Yes, what you have is a good start - once you fix compilation.
Preprocessor directives are not statements so should not be terminated with ;
There is a stray } at the end of the file.
You seem get that adding the full book details will be much the same. The key thing is that you need to find a book from the title. The simplest thing to do is to change from a vector<> to store the books to a map<>.
typedef std::string Title;
class BookDetails
{
// etc.
};
typedef std::map<Title, BookDetails> BookCollection;
Look up the documentation on std::map<> to see how it used.
Please try to pass parameters as a reference instead of passing by values. For eg.
instead of
void SetFirst(string First);
try to use
void SetFirst(string& First);
better still, if you are sure that the value is not going to change inside the function use like this SetFirst(const string& First);
This will save you from grief later.
Is this a console application or windows application that you are developing? If Console application the UI will be straight forward, but if Windows application you have to use either MFC or pure Win32.