My program is supposed take a user input to create a password. There's specific criteria.
The user should be able to retry up five times to create a "good" password. If they make a "good" password, the program should just end. If they don't make a password within the 5 tries, then the program should end. I tried using do-while and a for loop, but it won't break or end the program.
I have a text file with 5 passwords. The user can't use those 5 passwords. I put the passwords into an char array. I'm not sure how to compare the passwords in the file to the user input.
I can only use cstrings for this program. I'm not allowed to use any strings.
"prevPswds.txt"
2347UCDa!
PassUCD97~
#489sqUCD
123AaaUCD$%
UCDAsue1,
main.cpp:
#include <iostream>
#include <cstring>
#include <cctype>
#include <fstream>
//#include <string.h>
using namespace std;
int main()
{
//Initialize variables
const int size = 15;
char* check = NULL;
char checkUCD[] = "UCD";
char specialChar[] = { '~', '!', '#', '#', '$', '%', '^', '&', '*', '-', '_', '?' };
char password[size];
int counter=0;
bool length = false;
bool uppercase = false;
bool lowercase = false;
bool number = false;
bool special = false;
bool ucd = false;
bool previous = false;
bool lower = false;
bool upper = false;
bool num = false;
//bool done = false;
ifstream file("prevPswds.txt");
const int arraySize = 150;
char myArray[arraySize];
char current_char;
int count = 0;
//Read in file
for (int k = 0; k < arraySize; k++)
{
if (file.is_open())
{
int c = 0;
while (!file.eof())
{
file.get(myArray[c]);
c++;
count++;
}
}
}
for (int i = 0; i < 5; i++)
{
cout << "----CREATE A PASSWORD----" << endl;
cout << "Password requires:" << endl;
cout << "-8 to 12 characters" << endl;
cout << "-At least 1 uppercase letter" << endl;
cout << "-At least 1 lowercase letter" << endl;
cout << "-At least 1 number" << endl;
cout << "-At least 1 special character (~, !, #, #, $, %, ^, &, *, -, _, ?)" << endl;
cout << "-'UCD' \n" << endl;
cout << "Password cannot include:" << endl;
cout << "-Lowercase letters 'l,' 'i,' 'o,' or 'z'" << endl;
cout << "-Uppercase letters 'I,' 'O,' or 'S'" << endl;
cout << "-Numbers '0,' '1,' or '5'" << endl;
cout << "-------------------------" << endl;
//Get user input
cout << "Please enter a password." << endl;
cin.getline(password, size);
cout << endl;
counter++;
//Check requirements
if (strlen(password) < 8 || strlen(password) > 12)
{
cout << "-Password must be 8 - 12 characters." << endl;
}
else
{
length = true;
}
for (int i = 0; i < size; i++)
{
if (isupper(password[i])) //Check for uppercase
{
uppercase = true;
}
if (islower(password[i])) //Check for lowercase
{
lowercase = true;
}
if (isdigit(password[i])) //Check for numbers
{
number = true;
}
if (password[i] != 'l' || password[i] != 'i' || password[i] != 'o' || password[i] != 'z') //Check for exceptions
{
lower = true;
}
if (password[i] != 'I' || password[i] != 'O' || password[i] != 'S') //Exceptions
{
upper = true;
}
if (password[i] != '0' || password[i] != '1' || password[i] != '5') //Exceptions
{
num = true;
}
}
for (int i = 0; i < size; i++) //Check for special characters
{
if (specialChar[i])
{
if (ispunct(password[i]))
{
special = true;
}
}
}
check = strstr(password, checkUCD); //Check for 'UCD'
if (check)
{
ucd = true;
}
//Give feedback and suggestion
if (uppercase == false)
{
cout << "Password must contain at least 1 uppercase letter." << endl;
}
if (lowercase == false)
{
cout << "Password must contain at least 1 lowercase letter." << endl;
}
if (number == false)
{
cout << "Password must contain at least 1 number." << endl;
}
if (special == false)
{
cout << "Password must contain at least 1 special character." << endl;
}
if (ucd == false)
{
cout << "Password must contain 'UCD.'" << endl;
}
if (lower == false)
{
cout << "Password cannot contain 'l', 'i', 'o', or 'z.'" << endl;
}
if (upper == false)
{
cout << "Password cannot contain 'I', 'O', or 'S.'" << endl;
}
if (num == false)
{
cout << "Password cannot contain '0', '1' or '5.'" << endl;
}
}
if (length == true || uppercase == true || lowercase == true || number == true || special == true || ucd == true || previous == true || lower == true || upper == true || num == true)
{
return 1;
}
}
This is getting a bit long, so I have tried to add some small headers so you can jump to the next issue easier.
First thing first: Why is “using namespace std;” considered bad practice? Do yourself a favor and stop using it. It will be much harder to change if you let it become an ingrown habit, and at some point you will run into trouble and be forced to change.
Secondly, you have also run somewhat afoul of: Why is iostream::eof inside a loop condition (i.e. while (!stream.eof())) considered wrong? I think that your code actually does work, but I also think that it is a bit by chance.
for (int k = 0; k < arraySize; k++)
{
if (file.is_open())
{
int c = 0;
while (!file.eof())
{
file.get(myArray[c]);
c++;
count++;
}
}
}
The outer loop starts at k=0, then we arrive at the inner loop, where you read all the characters in the file - and then you read one character more, because eof is only set when you try to read beyond the end (with get). However, since you don't try to process that character here, you don't notice that the last read failed - though your count may be off by one.
Once the inner loop terminates, you then go back to the outer loop and set k=1. But since you have already read the whole file, the inner loop is simply skipped. And so on for k=2 to arraySize.
As you can see, the file reading has some problems, so don't do it like that. :)
Another consideration is that you probably don't want a single array of characters mashed together, it may be more desirable to have the five old passwords individually - and they are nicely all on their own line in the file.
My suggestion is that since you have 5 old passwords (can this number change?) that can be up to 12 or size (can this number change?) characters long, then you change myArray to char myArray[5][size]{}. Then use getline to read the old passwords, like so:
for (int k = 0; k < 5 && file.getline(myArray[k], size); k++)
{}
I think this will make your compare job easier, as you only have to loop over the five arrays, and compare myArray[k][i] == password[i].
Third issue is your checks. I would simplify them - unless you have a good reason for wanting to be able to see which ones failed later. Instead of:
if (isupper(password[i])) //Check for uppercase
{
uppercase = true;
}
if (islower(password[i])) //Check for lowercase
{
lowercase = true;
}
...
if (uppercase == false)
{
cout << "Password must contain at least 1 uppercase letter." << endl;
}
if (lowercase == false)
{
cout << "Password must contain at least 1 lowercase letter." << endl;
}
...
if (length == true || uppercase == true || lowercase == true || number == true || special == true || ucd == true || previous == true || lower == true || upper == true || num == true)
{
return 1;
}
Change it to:
if (!isupper(password[i])) //Check for uppercase
{
passwordIsGood = false;
cout << "Password must contain at least 1 uppercase letter." << endl;
}
if (!islower(password[i])) //Check for lowercase
{
passwordIsGood = false;
cout << "Password must contain at least 1 lowercase letter." << endl;
}
...
if (passwordIsGood)
{
return 1;
}
It will make your code more unified, and easier to read.
Fourth: It will also make it easier to fix the bug that you don't reset the booleans on each "create password" loop. You set f.ex. length = false when you declare the variable, then length = true after checking that it is correct. But then if that password fails on some other issue length is never reset, so the next password can be the wrong length but length is already true so the check fails.
The fifth problem is that you have placed your check if outside the password loop. The structure of your program is:
for (int i = 0; i < 5; i++)
{
cout << "----CREATE A PASSWORD----" << endl;
... lots of lines ...
}
if (length == true || uppercase == true || lowercase == true || number == true || special == true || ucd == true || previous == true || lower == true || upper == true || num == true)
{
return 1;
}
So you always loop five times through "CREATE A PASSWORD" before you check if the requirements are fulfilled.
My recomendation here is to refactor.
Move all your password checking into a function of its own:
bool checkPassword(char* password, int size)
{
if (strlen(password) < 8 || strlen(password) > 12)
{
cout << "-Password must be 8 - 12 characters." << endl;
return false;
}
for (int i = 0; i < size; i++)
{
if (!isupper(password[i])) //Check for uppercase
{
cout << "Password must contain at least 1 uppercase letter." << endl;
return false;
}
...
}
...
return true;
}
Then your loop becomes:
for (int i = 0; i < 5; i++)
{
cout << "----CREATE A PASSWORD----" << endl;
...
//Get user input
cout << "Please enter a password." << endl;
cin.getline(password, size);
cout << endl;
counter++;
//Check requirements
bool passwordIsGood = checkPassword(password, size);
if (passwordIsGood)
{
return 1;
}
}
Now it suddenly is much easier to see what is going on.
The sixth problem is that some of your checks don't do what you want. Specifically the ones like this:
if (password[i] != 'l' || password[i] != 'i' || password[i] != 'o' || password[i] != 'z')
Note that only one of the conditions in an OR has to be true for the OR to be true. If a=1, then a!=1 is false, but a!=2 is true. This means that a != 1 || a != 2 is true. a would have to be both 1 and 2 at the same time for a != 1 || a != 2 to be false.
One option for getting the behavior you want is to change the check to:
if (!(password[i] == 'l' || password[i] == 'i' || password[i] == 'o' || password[i] == 'z'))
That became way longer than I had intended... I hope it helps you at least some. :)
if (length == true || uppercase == true || lowercase == true || number == true || special == true || ucd == true || previous == true || lower == true || upper == true || num == true)
{
//return 1; take this line out and place after all {}
i = 5; //should end the for loop
}
}
return 1;
Related
I'm a beginner in c++, i'm trying to make a tictactoe. My program fails at the part acfter i enter input. When I enter an input, there is no next action from the program like i expected it to ("unvalid", check if win or loose, ask for input again). It only shows blank. like below: after I enter "1", nothing happens, and I can keep enter more input.
terminal example photo
I know it is a simple activity but I just ca't figure it out ToT. Thank you for helping!
//tictactoe
#include <iostream>
#include <vector>
using namespace std;
//declared variables
vector<char> out = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
int in = 2;
char player_out;
bool loose = false;
char x;
bool filled = false;
bool end(){ //won yet?
bool loose = false;
//horizontal
if (out[1] == out[2] && out[3] == out[2]){
loose = true;
}
else if (out[4] == out[5] && out[6] == out[5]){
loose = true;
}
else if (out[7] == out[8] && out[9] == out[8]){
loose = true;
}
//vertical
else if (out[1] == out[4] && out[7] == out[1]){
loose = true;
}
else if (out[2] == out[5] && out[8] == out[2]){
loose = true;
}
else if (out[3] == out[6] && out[9] == out[3]){
loose = true;
}
else if (out[1] == out[5] && out[9] == out[5]){
loose = true;
}
else if (out[3] == out[5] && out[7] == out[5]){
loose = true;
}
else{
loose = false;
}
return loose;
}
void game_start_display(){ //display the board
cout << "TIC TAC TOE\n";
cout << " | | \n";
cout << " " << out[1] << " | " << out[2] << " | " << out[3] << " \n";
cout << "______|______|______\n";
cout << " | | \n";
cout << " " << out[4] << " | " << out[5] << " | " << out[6] << " \n";
cout << "______|______|______\n";
cout << " | | \n";
cout << " " << out[7] << " | " << out[8] << " | " << out[9] << " \n";
cout << " | | \n\n";
}
int change_player(){ //take turn 1st and 2nd player
if (in == 1){
in++;
}
else{
in--;
}
return in;
}
bool filled_f() { //check if the spot is filled
if (out[x] != 'X' and out[x] != 'O'){
filled = true;
out[x] = player_out; //fill the input into the spot
}
else if (out[x] == 'X' or out[x] == 'O')
cout << "The square has already been used!\n";
filled = false;
return filled;
}
char player_out_f(){ //change output sign for each players (X, O)
if (in == 1){
player_out = 'X';
}
else if (in == 2){
player_out = 'O';
}
return player_out;
}
void c_player_display(){ //tell players to enter a number
cout << "Player " << in << "'s turn, please enter a number:\n";
}
int main(){
//intro
int loose = false;
game_start_display();
while(loose == false){ //when the game is still happening
change_player(); //change player (player start is set 2 so this comes first and change it to 1)
player_out_f(); //change player output sign (X, O)
c_player_display(); //print the line to ask for input
while(filled == false){ //when the there is no input yet (the spot is not filled)
cin >> x; // input
if (x > 0 && x < 10){ //check if input is in range 1-9
filled_f(); //check if the spot is occupied
}
else if(x < 0 && x > 10) { //if input is out of range
cout << "Invalid! Enter again!\n";
filled = false; //repeat the asking input circle (the while)
}
}
game_start_display(); //output the board again with new char (X or O)
end(); //check if anyone's won yet, if no, repeat the circle
}
cout << "Player " << in << " won! GG";
}
You have infinite loop at while (filled == false) { ... }, because filled_f always sets filled to false (and the else if branch of the condition inside this loop as well does so). It's because you most likely missed figure brackets when writing else if block in filled_f. Your indentation hints that you wanted 2 statements to be in that block, but as of now, only the first is executed conditionally, and the second (filled = false;) is executed after the branch. In other words, with intuitive indentation this function looks like this:
bool filled_f() { //check if the spot is filled
if (out[x] != 'X' and out[x] != 'O') {
filled = true;
out[x] = player_out; //fill the input into the spot
}
else if (out[x] == 'X' or out[x] == 'O')
cout << "The square has already been used!\n";
filled = false;
return filled;
}
It sets filled = false; in any case, since if/else execute (depending on condition) the statement immediately following one of them (see here), and indentation is ignored (unlike in, e.g., Python, where indentation alone determines boundaries of conditions, loops, function etc), so only cout << ... is conditionally executed. To do what you want put figure brackets around appropriate statements the same way you already did for the first if branch to create compound statement (block) from them, which is a statement itself and does what you want - groups several other statements within it, executed in sequence:
bool filled_f() { //check if the spot is filled
if (out[x] != 'X' and out[x] != 'O') {
filled = true;
out[x] = player_out; //fill the input into the spot
}
else if (out[x] == 'X' or out[x] == 'O') {
cout << "The square has already been used!\n";
filled = false;
}
return filled;
}
Additional remarks
Note that logically it's not needed to have if condition in else since if the first if condition is dissatisfied, else if condition is definitely satisfied (look De Morgan's Laws) and you can just write else:
// ...
else {
cout << "The square has already been used!\n";
filled = false;
}
// ...
Also in your main loop, you use this:
if (x > 0 && x < 10){ //check if input is in range 1-9
filled_f(); //check if the spot is occupied
}
else if(x < 0 && x > 10) { //if input is out of range
cout << "Invalid! Enter again!\n";
filled = false; //repeat the asking input circle (the while)
}
to test whether x is within range, but your condition in else if is wrong (should be x <= 0 || x >= 10 instead of x < 0 && x > 10) and can be omitted altogether (again see De Morgan's Laws) by using just else.
I am a beginner at coding and learning C++. I just wrote a code that asks the user to log in, Register, or exit.
I thought doing it through functions would be easier. While checking for the constraints of the username and password for registration(referred to as "Rusr" and "Rpwd" here) the code I've written to check if lowercase, uppercase, and digits are not working. I tried to do it with character array as well but that didn't work. Would really appreciate some help with this.
Here's my code below for further reference:
#include <fstream>
#include <iostream>
#include <string.h>
using namespace std;
bool isvalidName(string Rusr)
{
if(Rusr.length() > 5 && Rusr.length() < 20) // length constraint
return true;
else
cout << "Invalid username" << endl;
{
for(int i = 0; i < Rusr.length(); i++) // check if digits are present
{
if(isdigit(Rusr[i]) == true)
return true;
if(true)
break;
else
cout << "Invalid username";
}
}
{
for(int i = 0; i < Rusr.length(); i++) // check for lowercase
{
if(islower(Rusr[i])) {
return true;
}
if(true)
break;
else
cout << "Invalid username";
}
}
{
for(int i = 0; i < Rusr.length(); i++) // check for uppercase
{
if(isupper(Rusr[i])) return true;
if(true)
break;
else
cout << "Invalid username";
}
}
}
int isvalidPwd(string Rpwd) {
{
if(Rpwd.length() > 8 && Rpwd.length() < 20)
return true;
else
cout << "Invalid password " << endl;
}
{
for(int i = 0; i < Rpwd.length(); i++) {
if(isdigit(Rpwd[i]) == true) return true;
if(true)
break;
else
cout << "Invalid password";
}
}
{
if(!((Rpwd.find("#") == string::npos) ||
(Rpwd.find("#") == string::npos) ||
(Rpwd.find("!") == string::npos) ||
(Rpwd.find("$") == string::npos) ||
(Rpwd.find("%") == string::npos) ||
(Rpwd.find("^") == string::npos) ||
(Rpwd.find("&") == string::npos) ||
(Rpwd.find("*") == string::npos) ||
(Rpwd.find("(") == string::npos) ||
(Rpwd.find(")") == string::npos) ||
(Rpwd.find("_") == string::npos) ||
(Rpwd.find("+") == string::npos) ||
(Rpwd.find("|") == string::npos) ||
(Rpwd.find(">") == string::npos) ||
(Rpwd.find("<") == string::npos) ||
(Rpwd.find("?") == string::npos) ||
(Rpwd.find("/") == string::npos) ||
(Rpwd.find("~") == string::npos) ||
(Rpwd.find(".") == string::npos) ||
(Rpwd.find(",") == string::npos)))
return true;
else
cout << "should contain special characters" << endl;
}
for(int i = 0; i < Rpwd.length(); i++) {
if(islower(Rpwd[i])) return true;
if(true)
break;
else
cout << "should contain lower case";
}
{
for(int i = 0; i < Rpwd.length(); i++) {
if(isupper(Rpwd[i])) return true;
if(true)
break;
else
cout << "Should contain upper case";
}
}
}
int main() {
string Lusr, Lpwd, Rusr, Rpwd, name, pwd;
while(1) {
cout << "___________________________________" << endl;
cout << "Chose 1 to Register or 2 to Login" << endl;
cout << "___________________________________" << endl;
cout << "1.Register" << endl;
cout << "2.Login" << endl;
cout << "3.Exit" << endl;
cout << "___________________________________" << endl;
int choice;
cin >> choice;
if(choice == 1) // register
{
ofstream of("register.txt");
if(!of.is_open()) {
cout << "file not exist" << endl;
}
do {
cout << "Username should contain capital,small letters and "
"numbers. "
<< endl;
cout << "______________________________________" << endl;
cout << "Enter new username:" << endl;
cin.ignore();
getline(cin, Rusr);
isvalidName(Rusr);
} while(isvalidName(Rusr) == true);
do {
cout << "Password should contain capital,small letters,special "
"characters and numbers. "
<< endl;
cout << "_______________________________________" << endl;
cout << "Enter new passsword:" << endl;
getline(cin, Rpwd);
isvalidPwd(Rpwd);
} while(isvalidPwd(Rpwd) == true);
of << Rusr;
of << '\n';
of << Rpwd;
of.close();
}
else if(choice == 2) {
ifstream f("register.txt");
if(!f.is_open()) {
cout << "file not open" << endl;
}
getline(f, name, '\n');
getline(f, pwd, '\n');
f.close();
cout << "Enter username:" << endl;
cin.ignore();
getline(cin, Lusr);
cout << "Enter passsword:" << endl;
getline(cin, Lpwd);
if(Lpwd == pwd && Lusr == name) {
cout << "Welcome " << Lusr << endl;
;
break;
}
cout << "Wrong name and password" << endl;
}
else if(choice == 3) {
return 1;
} else {
cout << "Invalid input!Try again." << endl;
}
}
return 0;
}
With your code, some of your logic is wrong. When you say this
if(Rusr.length() > 5 && Rusr.length() < 20) // length constraint
return true;
else
cout << "Invalid username`enter code here`" << endl;
Any string that is between 5 and 20 characters will be accepted as a valid username. To fix this, you need to go through all your requirements, then return true. If any of the requirements fail, return false. This makes it so that the only way that the username will be valid is if it has already passed all the requirements.
bool isvalidName(string Rusr)
{
if(Rusr.length() < 5 || Rusr.length() > 20) // length constraint
{
std::cout << "invalid username length" << std::endl;
return false;
}
for(int i = 0; i < Rusr.length(); i++) // check if digits are present
{
if(isdigit(Rusr[i])) // if a digit is present, continue
{
break;
}
if(i == Rusr.length() - 1) // No digits were found
{
return false;
}
}
for(int i = 0; i < Rusr.length(); i++) // check for lowercase
{
if(islower(Rusr[i])) {
break;
}
if(i == Rusr.length() - 1) // No lowercase letters were found
{
cout << "Invalid username";
return false;
}
}
for(int i = 0; i < Rusr.length(); i++) // check for uppercase
{
if(isupper(Rusr[i]))
{
break;
}
if(i == Rusr.length() - 1) // No uppercase letters were found
{
cout << "Invalid username";
return false;
}
}
return true;
}
This way, the only way you return true is if it gets past all the constraints.
For the password, you would want to follow this model and adjust for those constraints.
Some other things to watch out for are the extra braces you have included. Best practice is to not change the scope further in than you need to, so you should avoid the braces in almost every case.
Both your functions fail to return the value you've declared that they should return in all branches, so your program has undefined behavior.
Also, the logic is flawed. Example:
bool isvalidName(string Rusr) {
if(Rusr.length() > 5 && Rusr.length() < 20) // length constraint
return true;
else
cout << "Invalid username`enter code here`" << endl;
// .. the rest ...
}
Here you check that the length is [6, 19] characters long - and return true; - that's all it takes to get Rusr approved. None of the other tests in your function will be executed if the name has an approved length.
Your code is riddled with similar mistakes.
for(int i = 0; i < Rusr.length(); i++) // check if digits are present
{
if(isdigit(Rusr[i]) == true) return true;
The first digit you encounter makes isvalidName return true - so now you have a name with an invalid length that contains a digit - approved!
Similar issue below. A name with an illegal length, without digits, that contains a lowercase letter is immediately approved:
for(int i = 0; i < Rusr.length(); i++) // check for lowercase
{
if(islower(Rusr[i])) {
return true;
And finally, if a name with an illegal length, without digits and without lowercase letters contains an uppercase letter, it's approved:
for(int i = 0; i < Rusr.length(); i++) // check for uppercase
{
if(isupper(Rusr[i])) return true;
If none of the above applies - you don't return anything (causing undefined behavior).
Your second function follows this pattern.
I'm a student, and I am currently working on C++ Classes. I am making a program which is supposed to ask a user to input a float point number not greater that 99.99 as a price of fuel at a gas station. I have created code that saves the user input in to a char array, and created limits so that the user can't input more than 2 dots, for example (2..2). The maximum number of characters is 5 including one dot. Now, everything works fine except for if the user enters two sets of strings before hitting enter. I have a problem because the second string messes up with other cin statements in the loop.
The code will also take the finalized char array input, and than covert it to a float variable so that the further calculations can be computed easily.
I am working on a Windows system, and Visual Studio 2017 C++.
I have tried detecting the single white space in an if/else statement, but it seems that white space is not detected as a single char array member, like this ex. else if (str[count] == ' ') , and than asking to re enter the correct input without the white space. getline() function could not work on a char array, so I couldn't discard the characters entered after including and after the white space in this way. I have tried changing the char array to a string, but still if the user inputs two or more strings separated by white space, my program keeps reading it in to cin over again.
int main()
{
int count = 0;
int lenFlag = 0, mainFlag = 0;
float result = 0;
int len;
char str[6] = "00000";
//string str ="00000";
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(2);
//This part will ask the user to set the price of fuel
//for the gas pump program. The programming project segment
//is from a book by Walter Savitch "Absolute C++".
while (mainFlag == 0)
{
cout << "Please enter the fuel price in dollars $";
cin >> str;
len = strlen(str);
cout << "strlen is = " << len << endl;
while (len <= 5 && mainFlag == 0)
{
count = 0, lenFlag = 0;
while (count < len && lenFlag == 0)
{
if (count == 0 && (str[count] < 48 || str[count] > 57))
{
cout << "The first input member must be a number."
"You must use a number between 0-9.\n"
"Try again: ";
cin >> str;
len = strlen(str);
lenFlag = 1;
}
else if (count > 0 && (str[count] < 48 || str[count] > 57)
&& str[count] != '.')
{
cout << "You must enter number between 0-9, or a decimal delimiter.\n"
"Try again, : ";
cin >> str;
len = strlen(str);
lenFlag = 1;
}
else if (count > 0 && (str[0] == '.' && str[1] == '.') || (str[0] == '.' && str[2] == '.') ||
(str[0] == '.' && str[3] == '.') || (str[0] == '.' && str[4] == '.') ||
(str[1] == '.' && str[2] == '.') || (str[1] == '.' && str[3] == '.') ||
(str[1] == '.' && str[4] == '.') || (str[2] == '.' && str[3] == '.') ||
(str[2] == '.' && str[4] == '.') || (str[3] == '.' && str[4] == '.'))
{
cout << "You have entered more than 1 decimal delimiter, try again: ";
cin >> str;
len = strlen(str);
lenFlag = 1;
}
else if (count > 1 && str[0] > 48 && str[0] < 58 && str[1]>47
&& str[1] < 58 && str[2]>47 && str[2] < 58)
{
cout << "Maximum number is 99.99, try again:\n";
cin >> str;
len = strlen(str);
lenFlag = 1;
}
else if (str[count] == ' ')
{
cout << "Typing whitspace is not an option!!" << endl;
cout << "Try again!!" << endl;
cin >> str;
len = strlen(str);
lenFlag = 1;
}
else if (count == len - 1 && lenFlag == 0)
{
//cout << "Main flag switches to 1!!" << endl;
mainFlag = 1;
}
count++;
}
} //while(lenCopy <= 5) loop end
if (len > 5)
{
cout << "Either non-numbers were entered, or a negative
number, or an incorrect " << endl;
cout << "number size. Enter a maximum size of 5
including a .dot for decimal number" << endl;
cout << "Maximum number is 99.99." << endl;
mainFlag = 0;
}
}//mainflag loop ends
int dotpos = 0;
for (int n = 0; n < len; n++)
{
if (str[n] == '.')
{
//dotpos = n + 1;
dotpos = len - n - 1;
cout << "dotpos = " << dotpos << endl;
}
else
{
result = result * 10 + (str[n] - '0');
//Line above is a float and character mix as a math equation.
cout << "result " << n << " = " << result << endl;
}
}
if (dotpos > 0)
result = result / (power(10, dotpos));
cout << "You have set the cost at $" << result << " per gallon." << endl;
system("pause");
return 0;
}
Occasional stack around str variable has been corrupted, and that happens when I heavily try to mess with the user input just to check if the program can crash. That's why I need to know how to clear the input after the white space. I solved the stack corruption problem by changing the char array to string, but still not the excess characters that potential users might throw down at the program.
If you must use character arrays, I highly recommend restricting the amount of characters read from the console.
The std::istream::getline() is well suited for this:
const unsigned int LIMIT = 10;
char number_as_text[LIMIT];
std::cout << "Enter a floating point number, less than 10 characters: ";
std::cin.getline(number_as_text, LIMIT);
You can then use a function like strtod to convert the string to floating point variable.
I have found one good way to solve a problem of the string buffer overflow. It uses
cin>>ws; followed by getline() function. The two need to be used in conjunction, and
than the read will be only the first string of characters and everything after the whitespace will be trashed.
cout << "Do you want to set cost in gallons or liters? "
"\nPress G for gallons or L for liters: ";
cin >> ws;
getline(cin, chooseSetCost);
while (chooseSetCost != "G" && chooseSetCost != "g" && chooseSetCost != "L" && chooseSetCost != "l")
{
cout << "Incorrect input. Try again: ";
cin >> ws;
getline(cin, chooseSetCost);
cout << "choose cost is = " << chooseSetCost << endl;
}
I am trying to make a password validator that tests for the following criteria:
The password should be at least 8 characters long
The password should contain at least one uppercase and at least one lowercase
The password should have at least one digit
The password should 1 special character: ! # # $ % & * : ;
I got the program to work WITHOUT functions, so I tried to make a function that tests each criteria but for some reason strlen will not work inside my function. I included cstring and cctype but I can't figure out why the error persists. Please help out!
#include <iostream>
#include <cctype>
#include <cstring>
using namespace std;
void criteraValidation(string str1, int up, int low, int digit, int special);
int main()
{
const int SIZE = 20;
char password[SIZE];
int Upper, Lower, Digit, SpecialChar;
cout << "Password Validation Criteria" << endl;
cout << "The password should be at least 8 characters long" << endl;
cout << "The password should contain at least one uppercase and at least one lowercase letter" << endl;
cout << "The password should have at least one digit" << endl;
cout << "The password should 1 special character: ! # # $ % & * : ; " << endl;
do
{
Upper = Lower = Digit = SpecialChar = 0;
cout << "Enter password: ";
cin.getline(password, SIZE);
}
while (SpecialChar == 0 || Upper == 0 || Lower == 0 || Digit == 0 || strlen(password) < 8);
cout << "All crieria have been met! Password: " << password << endl;
return 0;
}
void criteraValidation(string str1, int up, int low, int digit, int special)
{
for (int i = 0; i < strlen(str1); i++)
{
if (isupper(str1[i])) // checks uppercase letters
up++;
if (islower(str1[i])) // checks lowercase letters
low++;
if(isdigit(str1[i])) // checks digits
digit++;
if (ispunct(str1[i])) // checks special characters
special++;
}
if (strlen(str1) < 8)
cout << "Must be at least 8 characters long.\n";
if (up == 0)
cout << "Must be at least 1 uppercase letter.\n";
if (low == 0)
cout << "Must be at least 1 lowercase letter.\n";
if (digit == 0)
cout << "Must have at least 1 digit.\n";
if (special == 0)
cout << "Must have 1 special character" << endl;
}
From the comments, you may want something like this:
void criteraValidation(const string& str1, // Pass by const reference since it won't be modified.
int& up, int& low, int& digit, int& special);
void criteraValidation(const string& str1,
int& up, int& low, int& digit, int& special)
{
up = 0;
low = 0;
digit = 0;
special = 0;
const std::string::size_type length = str1.length();
for (int i = 0; i < length; i++)
{
if (isupper(str1[i])) // checks uppercase letters
up++;
if (islower(str1[i])) // checks lowercase letters
low++;
if(isdigit(str1[i])) // checks digits
digit++;
if (ispunct(str1[i])) // checks special characters
special++;
}
if (str1.length() < 8)
cout << "Must be at least 8 characters long.\n";
if (up == 0)
cout << "Must be at least 1 uppercase letter.\n";
if (low == 0)
cout << "Must be at least 1 lowercase letter.\n";
if (digit == 0)
cout << "Must have at least 1 digit.\n";
if (special == 0)
cout << "Must have 1 special character" << endl;
}
In main:
do
{
Upper = Lower = Digit = SpecialChar = 0;
cout << "Enter password: ";
cin.getline(password, SIZE);
criteraValidation(password, Upper, Lower, Digit, SpecialChar);
}
while (SpecialChar == 0
|| Upper == 0
|| Lower == 0
|| Digit == 0
|| (password.length() < 8));
Note: The isupper, islower, isdigit and ispunct are exclusive (a character can't be both upper and lower case), so you may want to use an if-else-if ladder so that not all comparisons are always executed:
const char c = str1[i];
if (isupper(c))
{
++up;
}
else if (islower(c))
{
++low;
}
else if (isdigit(c))
{
++digit;
}
else if (ispunct(c))
{
++special;
}
else
{
// Put code here for characters not of above types.
}
For example, for the character 'A' only one function is executed, whereas all the comparisons are executed in your code.
your example shows strlen() working on an array of char. When you pass it to a function you're sending in a 'string'. Is that a std::string? do you need to call strlen(str1.c_str())?
I am trying to write a c++ program that validates a password that requires one uppercase letter, one lowercase letter, and a digit using functions.
The issue is that I'm trying to display the specific errors that are occurring, not just "Invalid, try again.", but I'm having a hard time figuring out how to do that. It should keep asking until they enter a valid password.
#include<iostream>
#include<string>
#include<cctype>
using namespace std;
int validate(string);
string find(int);
int main()
{
string pw;
int val;
string result;
do{
cout << "Enter password: " << endl;
cin >> pw;
val = validate(pw);
cout << find(val) << endl;
} while (val != 0);
}
//VALIDATES PASSWORD
int validate(string pw)
{
int valid = 0;
char c;
int length = pw.length();
bool digit = false;
bool upper = false;
bool lower = false;
int i = 0;
if (pw.length() < 6)
valid = 1;
while (i < pw.length())
{
c = pw[i];
i++;
if (isdigit(c))
{
digit = true;
valid++;
}
if (isupper(c))
{
upper = true;
valid++;
}
if (islower(c))
{
lower = true;
valid++;
}
//Valid input
if (length >= 6 && upper && lower && digit)
valid = 0;
}
return valid;
}
//RETURNS STRING WITH PROBLEM
string find(int valid)
{
string result;
if (valid == 0)
{
result = "Valid Password ";
}
else
{
result = "Invalid Password: ";
if (valid == 1)
result = result + " Too short ";
else if (valid == 2)
result = result + " too short, needs a digit, and an uppercase letter";
else if (valid == 3)
result = result + " too short, needs a digit, and a lowercase letter";
else if (valid == 4)
result = result + " too short, and needs an uppercase letter";
else if (valid == 5)
result = result + " too short, and needs a lowercase letter";
else if (valid == 6)
result = result + " too short, needs a digit";
else if (valid == 7)
result = result + " Needs a didgit ";
else if (valid == 8)
result = result + " Needs digit and uppercase letter ";
else if (valid == 9)
result = result + " Needs digit and lowercase letter";
else if (valid == 10)
result = result + " Needs an uppercase letter ";
else if (valid == 11)
result = result + " Needs uppercase and lowercase letter";
else if (valid == 12)
result = result + " Needs a lowercase letter";
}
return result;
}
I think you are confusing the number of characters (valid) and the type of error -
else if (valid == 9)
result = result + " Needs digit and lowercase letter";
could be produced from 123456abc
As valid == 9 really only counts the characters in the set. Separate counting and whether character classes are used.
It's better to use some flags (bool variables) instead of one number.
If you want to use one number, you must create 2^(things to check) situations
using that number. Here 2^4 = 16 situations is required.
One of the easiest way to mix flags in one number is this:
nth digit = nth flag
for example use this order (length, lower, upper, digit).
So,
to set length validity, add 1000 to number;
to set lower validity, add 100 to number;
to set upper validity, add 10 to number;
to set digit validity, add 1 to number;
Now,
((number)%10 == 1) means digit validity
((number/10)%10 == 1) means upper validity
((number/100)%10 == 1) means lower validity
((number/1000)%10 == 1) means length validity
Following code uses separate flags:
#include<iostream>
#include<string>
using namespace std;
class PasswordStatus
{
bool len, low, up, dig;
//============
public:
PasswordStatus()
{
len = low = up = dig = false;
}
//-----------
void ShowStatus()
{
cout << endl << "Password Status:" << endl;
cout << "Length : " << (len ? "OK" : "Too Short") << endl;
cout << "Contains Lower Case : " << (low ? "Yes" : "No") << endl;
cout << "Contains Upper Case : " << (up ? "Yes" : "No") << endl;
cout << "Contains Digit : " << (dig ? "Yes" : "No") << endl;
}
//-----------
void checkValidity(string pass)
{
int sLen = pass.length();
len = (sLen >= 6);
for(int i = 0; i<sLen; i++)
{
char c = pass[i];
if(!low && islower(c)) {low = true; continue;}
if(!up && isupper(c)) {up = true; continue;}
if(!dig && isdigit(c)) {dig = true; continue;}
}
}
//-----------
bool IsTotalyValid()
{
return low && up && dig && len;
}
};
//====================================================================
int main()
{
PasswordStatus ps;
string pw;
do
{
cout << endl << "Enter password: " << endl;
cin >> pw;
ps.checkValidity(pw);
ps.ShowStatus();
} while (!ps.IsTotalyValid());
cout << "Valid Password : " << pw;
return 0;
}