I'm a beginner to C++. We are doing a project where we input firstName, lastName, and SSN for employee. Here what I have done:
#include <iostream>
#include <stdexcept>
#include "Employee.h"
using namespace std;
Employee::Employee(const string &first, const string &last, const string &ssn)
{
firstName = first;
lastName = last;
SSN = ssn;
}
void Employee::setFirstName(const string &first)
{
firstName = first;
}
string Employee::getFirstName() const
{
return firstName;
}
void Employee::setLastName(const string &last)
{
lastName = last;
}
string Employee::getLastName() const
{
return lastName;
}
void Employee::setSSN(const string &ssn)
{
if (ssn.length() == 9)
{
SSN = ssn;
}
else
{
cout << "Please enter SSN again: " << endl;
}
}
string Employee::getSSN() const
{
return SSN;
}
void Employee::print() const
{
cout << "Employee: " << getFirstName() << ' ' << getLastName()
<< "\nSocial Security Number: " << getSSN();
}
My instructor wants us to check the length of the SSN (simplest way) to make sure it 9 digits, and if it's more or less, ask users to input again. I do not know how to validate the input for SSN. Can anyone help me please?
std::string::length() is the function to be used to find length and std::string::empty() to check weather string is empty or not.
if(SSN.length() < 9 && !SSN.empty())
{
//Need C++11 and above for this kind for loop
for(auto &x: SSN)
{
if(std::isdigit(x))
{
//valid SSN
}
}
}
A compact solution to this problem:
bool ValidSSN(const std::string& ssn) {
if (ssn.size() != 9) return false;
return ssn.find_first_not_of("0123456789") == ssn.npos;
}
There is no simple and easy solution to checking the string for 9 digits.
My recommendation is a two step process:
Verify length is 9.
Verify string is an integral number.
Note: The code in the else clause does not work for cases such as "123Apple4". Thanks to #Steephen for the enlightenment. Leaving the code in the answer to synchronize with comments.
In code this would be:
bool is_valid = true;
if (ssn.length() != 9)
{
is_valid = false;
}
else
{
std::istringstream ssn_stream;
ssn_stream.str(ssn);
unsigned int value;
if (!(ssn_stream >> value))
{
is_valid = false;
}
}
Another alternative is to use a loop:
if (ssn.length() != 9)
{
is_valid = false;
}
else
{
is_valid = true;
for (unsigned int i = 0U; i < 9; ++i)
{
if (!isdigit(ssn[i]))
{
is_valid = false;
break;
}
}
}
Edit 1: Regular Expressions
If your compiler supports C++ regular expression, you could come up with a regular expression to define the SSN. You would then check if the contents of the string matched the regular expression.
Related
My question is to both:
Allow the user to enter a name and get custom messages based upon the name entered, and, when you collect the name from the user, convert the collected name to lowercase.
Thus, in my 'void test_user_details()' function, I made a custom message if the user puts in either Tim or John, but when I test the code it doesnt work? It skips the custom message. Also, I used "to_lowercase(name)" but the output isnt lowercase, it stays the same.
It seems like my if and else if functions arnt running at all and I dont know why.
Here is the full code of the program:
#include "splashkit.h"
#include <string>
using namespace std;
string read_string(string prompt)
{
string result;
write(prompt);
result=read_line();
return result;
}
int read_integer(string prompt)
{
string line;
line = read_string(prompt);
return convert_to_integer(line);
}
***************
void test_user_details()
{
string name;
string line;
int age;
name = read_string("What is your name: ");
line = read_string("What is your age: ");
age = convert_to_integer(line);
write_line("Hello, " + name);
if (to_lowercase(name) == "Tim")
{
write_line("\nWelcome Tim, creator of this program");
}
else if (to_lowercase(name) == "John")
{
write_line("\nWelcome John, tester of this program");
}
else
{
write_line("Are you Tim or John?");
}
write("Age: ");
write_line(age);
}
void play_game()
{
string line;
int guess;
int target;
target = rnd(100) + 1;
write_line("Guess a number between 1 and 100 (inclusive)");
while(guess != target)
{
write("Enter guess: ");
line = read_line();
guess = convert_to_integer(line);
if (guess < target)
{
write_line("Sorry, the number is greater than " +
to_string(guess));
}
else if (guess > target)
{
write_line("No, the number is less than " +
to_string(guess));
}
}
write_line("You guessed correctly! The number was " +
to_string(target));
}
int main()
{
string line;
int option;
do
{
write_line("1. Play Game");
write_line("2. Quit");
write("Choosen option: ");
line = read_line();
option = convert_to_integer(line);
switch(option) //switch function
{
case 1:
test_user_details();
play_game();
break;
case 2:
write_line("Game ends.");
break;
default:
write_line("Please enter an option from the menu");
}
} while (option!= 2);
return 0;
}
assuming your to_lowercase returns a string in lower case it will never match:
"Tim" or "John"
This is my school program that crashes on me, probably something from (Segmentation fault / Bus error / Memory limit exceeded / Stack limit exceeded). I can't figure out where the mistake is. I tried to comment on the code and reduce it a bit.
Retrieving information from the file in the form [name] [surname] [number]
Martin Jeff 123456789
Tomas Adam 234567890
This is followed by a blank line [\ n]
And then I search by the entered name, surname or both
Martin
Thomas
Adam
Martin Jeff
...
Thank you in advance for your advice.
class Uzivatel
{
public:
string name;
string surname;
string number;
};
void alocation(int &velPole, Uzivatel *arr)
{
velPole = 2*velPole;
Uzivatel *tmp = new Uzivatel[velPole];
arr = tmp;
delete []tmp;
}
void getString(const string & fileName, ostream &strStream)
{
ifstream ifs;
ifs.open(fileName);
if(ifs.is_open())
{
strStream << ifs.rdbuf();
ifs.close();
}
}
void finding(int pocet, Uzivatel *uzivatele, ostream &out, string &line)
{
string name = ""; string surname = ""; string number = "";
int matches = 0;
stringstream s(line);
s >> name >> surname >> number;
if(!(surname.compare("")))
surname = name;
for(int i=0; i<pocet; i++)
{
if( (!name.compare(uzivatele[i].name)) || (!surname.compare(uzivatele[i].surname)) )
{
out << uzivatele[i].name << " " << uzivatele[i].surname << " " << uzivatele[i].number << endl;
matches++ ;
}
}
out << "-> " << matches <<endl;
}
bool isValid(string &jmeno, string &prijmeni, string &cislo)
{
int x = cislo.find_first_not_of("0123456789");
int y = cislo.length();
if((!x) || cislo[0] == '0' || y != 9 || jmeno=="" || prijmeni=="" || cislo=="" )
return false;
return true;
}
bool report ( const string & fileName, ostream & out )
{
ifstream ifs;
stringstream strStream;
getString(fileName, strStream);
int arrLen = 200;
Uzivatel *uzivatele = new Uzivatel[arrLen];
int arrElem = 0;
string line;
bool hledani = false;
while (getline(strStream, line))
{
if(hledani)
{
finding(arrElem, uzivatele, out, line);
}
else
{
stringstream s(line);
string konec = "";
s >> uzivatele[arrElem].name >> uzivatele[arrElem].surname >> uzivatele[arrElem].number >> konec;
/* If there was anything else at the entrance*/
if(konec!="")
{
delete []uzivatele;
return false;
}
/* Realloc */
if(arrElem == arrLen)
alocation(arrLen, uzivatele);
arrElem++;
/* Enter'\n' */
if(!line.compare(""))
{
hledani = true;
arrElem--;
/* Validation enter */
for(int i=0; i<arrElem;i++)
{
if(!(isValid(uzivatele[i].name, uzivatele[i].surname, uzivatele[i].number)))
{
delete []uzivatele;
return false;
}
}
}
}
}
if(!hledani)
{
delete []uzivatele;
return false;
}
delete []uzivatele;
return true;
}
int main ()
{
ostringstream oss;
oss . str ( "" );
assert ( report( "tests/test0_in.txt", oss ) == true );
assert ( oss . str () ==
"John Christescu 258452362\n"
"John Harmson 861647702\n"
"-> 2\n"
"-> 0\n"
"Josh Dakhov 264112084\n"
"Dakhov Speechley 865216101\n"
"-> 2\n"
"John Harmson 861647702\n"
"-> 1\n" );
oss . str ( "" );
assert ( report( "tests/test1_in.txt", oss ) == false );
return 0;
}
Test0 (Work correct):
John Christescu 258452362
Peter Herreran 716973426
Josh Dakhov 264112084
John Harmson 861647702
Dakhov Speechley 865216101
John
Martin
Dakhov
Harmson
You code as posted with the input data you gave (those 2 lines) works, except that the second assert fails. The reason being that you never write to the out stream in 'report'.
The reason it fails with a larger data set is due to this function
void alocation(int& velPole, Uzivatel* arr)
{
velPole = 2 * velPole;
Uzivatel* tmp = new Uzivatel[velPole];
arr = tmp;
delete[]tmp;
}
This function does nothing. It allocates a new larger array of Uzi objects, then deletes it. I assume its actually trying to delete the old one thats too small
You should return the new pointer after deleting the old one.
Much better would be std::vector which will do this all for you
Also why read the whole file into memory as a stringstream then read the stringstream, why not use the file stream directly?
It is possible to do next:
Let say I have a string "input" (that will be a input), I will cut this input in 2 parts, next, I will find if I entered first part only letters, and second part only digits? the code work only for letters but not for digits(remove comments, to see that all entered will be valid)
#include <iostream>
#include <string>
#include <iomanip>
#include <cctype>
using namespace std;
int main ()
{
while (true)
{
bool flag = false; // to check for numeric entry
string input; // not req to initialize
string input1;
cout << "Enter the string like ABC 123: ";
getline (cin, input);
if (input == "")
{
flag = true;
}
if (string::size_type pos = input.find (' '))//spliting the input in 2 if it will find a space
{
if (input.npos != pos)
{
input1 = input.substr (pos + 1);
input = input.substr (0, pos);
}
}
for (int i = 0; i < input.size (); i++)
{
// for (int n = 0; i < input1.size (); i++)
// {
int uppercaseCHar = toupper (input[i]);//checking if input(first part) contains only letters
if (!std::isalpha (uppercaseCHar))
{
// if(isdigit(input1[n]) == 0)//checing if input1(second part) contains only digits
// {
flag = true;
break;
// }
}
// }
}
if (input.compare ("1") == 0) break;//This will end program
{
flag = false;
}
if (flag)
{
cout << "Invalid!\n";
cout << endl;
} else
{
cout << "The string is valid! \n";
cout << endl;
}
}
}
Enter the string like ABC 123: QWE 123
The string is valid!
Enter the string like ABC 123: QW1 123
Invalid!
I don't have rights to comment yet, but if your first and second part of string don't have to be of same length, you can use the for loop two times for each sub string and then compare the values by one character at a time.
Also after checking for termination condition by
if (input.compare ("1") == 0) break;
you are adding a statement
{
flag = false;
}
this will set the result to false even if you have compared in your loop and found it TRUE,so take a look at following code below, i have commented that block of code out.
#include <iostream>
#include <string>
#include <iomanip>
#include <cctype>
using namespace std;
int main ()
{
while (true)
{
bool flag = false; // to check for numeric entry
string input; // not req to initialize
string input1;
cout << "Enter the string like ABC 123: ";
getline (cin, input);
if (input == "")
{
flag = true;
}
if (string::size_type pos = input.find (' '))//spliting the input in 2 if it will find a space
{
if (input.npos != pos)
{
input1 = input.substr (pos + 1);
input = input.substr (0, pos);
}
}
//cout<<"\n"<<input1;
//cout<<"\n"<<input;
//First check the letter part (first part) if it contains digits
for(int i=0;i<input.size();i++){
if(!std::isalpha(input[i])){
flag=true;
break;
}
}
//second check if the numeric part (second part) only contains digits
for(int i=0;i<input1.size();i++){
if(!std::isdigit(input1[i])){
flag=true;
break;
}
}
/*
for (int i = 0; i < input.size (); i++)
{
for (int n = 0; i < input1.size (); i++)
{
int uppercaseCHar = toupper (input[i]);//checking if input(first part) contains only letters
if (!std::isalpha (uppercaseCHar))
{
if(isdigit(input1[n]) == 0)//checing if input1(second part) contains only digits
{
flag = true;
break;
}
}
}
}
*/
if (input.compare ("1") == 0) break;//This will end program
//after checking for break, if you add flag=false, it will automatically ignore whatever flag you set, and you will always find FLAG=FALSE when compairing in upcoming lines
//{
// flag = false;
//}
if (flag)
{
cout << "Invalid!\n";
cout << endl;
} else
{
cout << "The string is valid! \n";
cout << endl;
}
}
}
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
I am trying to allow only integrers to be inputted
Should Reject:
5h
3.4
3.gh
3.0
htr
Should Accept:
-5
0
78
Current Code
int getIntInput() {
int userInput;
while (true) {
std::cout << "> ";
std::cin >> userInput;
std::cout << std::flush;
if (std::cin.fail()) {
std::string cinBuffer;
std::cin.clear();
std::getline(std::cin, cinBuffer);
continue;
}
break;
}
return userInput;
}
Updated Code
Issues:
Accepts all rejections excluding "htr" (No numerals)
int getIntInput() {
std::string rawInput;
int parsedinput;
while (true) {
std::cout << "> ";
std::getline(std::cin, rawInput);
std::cout << std::flush;
try {
parsedinput = std::stoi(rawInput);
} catch (std::invalid_argument & e) {
continue;
} catch (std::out_of_range & e) {
continue;
}
break;
}
return parsedinput;
}
Finished Code
Accepts only integers with an optional parameter which will allow
negative numbers to be accepted or rejected.
int getIntInput(bool allowNegatives = true) {
bool validIntInput;
std::string rawInput;
int parsedinput;
while (true) {
validIntInput = true;
// Grabs the entire input line
std::cout << "> ";
std::getline(std::cin, rawInput);
std::cout << std::flush;
for (int i = 0; i < rawInput.length(); i++) {
// Checks to see if all digits are a number as well as to see if the number is a negative number
if (!isdigit(rawInput[i]) && !(allowNegatives && i == 0 && rawInput[i] == '-')) {
validIntInput = false;
break;
}
}
if (!validIntInput) {
continue;
} else {
try {
// Try parse the string to an int
parsedinput = std::stoi(rawInput);
// Catch all possible exceptions, another input will be required
} catch (...) {
continue;
}
// If the code reaches here then the string has been parsed to an int
break;
}
}
return parsedinput;}
The way cin works when having to read an int, is that it starts parsing the integer and stops when it finds a non-int character, storing the parsed int in the variable. That's why on float numbers it stops on the dot and in input '5g' it will stop on g.
What you can do instead if you only want integer input, is to read the whole line and then check if every character in your string is numeric, with the following piece of code:
bool onlyNums = true;
for (int i=0;i<rawInput.size();i++) {
if (!isdigit(rawInput[i]))
onlyNums = false;
}
if (!onlyNums)
continue;
(You have to include ctype.h library for the above code)
If you don't mind the overhead, I would grab the input from cin with cin.getline() and save it into a string. Then loop through the string and call isdigit on each char. You can discard the chars that aren't digits by useing the str.erase function.
You will need to #include cctype for isdigit().
Note: this will have at least O(N) runtime based on the length of your string.
template<class T>
T findInStreamLineWith(std::istream &input) {
std::string line;
while (std::getline(input, line)) {
std::istringstream stream(line);
T x;
if (stream >> x >> std::ws && stream.eof()) {
return x;
}
// reenter value
// cout << "Enter value again: ";
}
throw std::invalid_argument("can't find value in stream");
}
…
auto x = findInStreamLineWith<int>(std::cin);