Strlen not working when I put it into a function - c++

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())?

Related

How do you find the number of occurences of a certain word in a sentence when reading in the sentence character by character?

For example, if I wanted to find the number of times that the word "MY" appears in a user-inputted sentence, how would I do that? Is it even possible to do this if I'm reading in the sentence one character at a time with a while-loop?
Sample input would be something like: "My house is here"
My current output is:
Number of words.........4
Number of uppercase letters.........1
Number of lowercase letters........12
Number of vowels.........6
Number of substring MY.........0
where number of substring MY should be 1.
Here's what I currently have:
#include <iostream>
#include <string>
#include <cstring>
#include <iomanip>
using namespace std;
bool isvowel(char c) {
if (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U' || c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
return true;
} else {
return false;
}
}
int main() {
char c;
int words = 0;
int upperCount = 0;
int lowerCount = 0;
int vowels = 0;
int my = 0;
cout << "Enter a sentence: ";
while (cin.get(c), c != '\n') {
if (isupper(c)) {
upperCount++;
}
if (islower(c)) {
lowerCount++;
}
if (isspace(c)) {
words++;
}
if (isvowel(c) == true) {
vowels++;
}
if (c == 'M' || c == 'm') {
if (c+1 == 'Y' || c+1 == 'y') {
my++;
}
}
}
cout << left << "Number of words" << setfill('.') << setw(10) << right << words + 1 << endl;
cout << left << "Number of uppercase letters" << setfill('.') << setw(10) << right << upperCount << endl;
cout << left << "Number of lowercase letters" << setfill('.') << setw(10) << right << lowerCount << endl;
cout << left << "Number of vowels" << setfill('.') << setw(10) << right << vowels << endl;
cout << left << "Number of substring MY" << setfill('.') << setw(10) << right << my << endl;
system("Pause");
return 0;
}
This can be done in many ways, you almost have one. I will not give you the exact solution but you can try something like this: (written in Java)
// String sentence = "My house is here";
// word = "My"
private int getWordCount(String sentence, String word) {
char[] charArr = sentence.toCharArray();
String currWord = "";
int count = 0;
for(char c : charArr) {
if(c != ' ') { currWord += c; } // if c is not space it gets appended to the current word
else {
if(currWord.toLowerCase().equals(word.toLowerCase())) {
count++;
}
currWord = "";
}
}
return count;
}
Keep a track of the current string. If the current character is not a whitespace, append it to the string; else, the string becomes empty.
For each string, you could compare it to the target string. This comparison will have O(n) complexity, where n is the length of the string.
To optimise it further, you could build a trie for the target string. Since you're already processing one character at a time, the string matching could be done in O(1) instead of O(n).

How to compare char array to cstring and loop program

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;

Pig latin conversion using Cstrings

The program takes in a word given by the user and translates that to pig latin. I've gotten everything to work almost perfectly, but have run into two bugs. The first of which is when translating words that begin with consonants say "count", the output is "ounttcay" instead of "ountcay". The second bug is that when for three letter words like "egg" or "not" the output is "egg_\377ay" or "ottn\377ay". Is there a simple way to remove that duplicate character and get rid of those numbers?
Note - Unfortunately it has to be done using a Cstring
#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;
int convertToPigLatin(char arr[50]);
bool isVowel(char ch);
int main() {
char userInput[50];
char answer = ' ';
do {
cout << "Enter a word to convert it to pig latin" << endl;
cin.getline(userInput, 50); //get user input
cout << "Your entered word is " << userInput << endl;
convertToPigLatin(userInput); //translate user's input into piglatin
cout << "Would you like to convert another word?" << endl;
cin >> answer;
cin.ignore(); //clear past user input
cin.clear();
} while (answer == 'Y' || answer == 'y');
return 0;
}
bool isVowel (char ch) {
switch (tolower(ch)) { //if the first character of the given input is a vowel
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
return true;
default:
return false;
}
}
int convertToPigLatin(char arr[50]) {
char newArr[50];
// string conjunctions[6] = {"and","but","for","nor","yet","the"}; //list of conjunctions not to be converted
size_t arrLength = strlen(arr); //holds length of input
for (int i = 0; i < arrLength; i++) { //make sure all characters in input are lower case for easier processing
newArr[i] = tolower(arr[i]);
}
char lastChar = newArr[0]; //save the first character in case it needs to be appended
if (atoi(arr) || arr[0] == '\0') { //if the input contains a number or begins with a null character print an error
cout << "Cannot translate inputs that contain numbers" << endl;
return -1;
} else if (arrLength <= 2) { // if the input is 2 or less characters
cout << newArr << endl; //print the input as is
cout << "Boring! Try somthing more than 2 characters long" << endl;
return 0;
} else if ((strstr(newArr, "and") && arrLength == 3) || (arrLength == 3 && strstr(newArr, "but")) || (arrLength == 3 && strstr(newArr, "for")) || (arrLength == 3 && strstr(newArr, "nor")) || (arrLength == 3 && strstr(newArr, "yet")) || (arrLength == 3 && strstr(newArr, "the"))) { //if the input is more than 2 characters long
cout << newArr << endl; //print the input as is
cout << "No conjucntions try again!" << endl;
return 0;
} else { //if the given input is three characters and is not a conjunction, being translation
if (isVowel(arr[0])) { //check if input's first character is a vowel
cout << "Your word in piglatin is "<< strcat(newArr, "ay") << endl; //print that string with 'ay' at the end (i.e. egg'ay')
return 0;
} else { //else if the given input starts with a consonant
for (int r = 1; r < arrLength; r++) {
newArr[r-1] = newArr[r];
newArr[arrLength] = lastChar;
}
cout << "Your word in piglatin is " << strcat(newArr, "ay") << endl;
return 0;
}
}
return 0;
}
You're not terminating newArr, and the last index of the input string is arrLength - 1.
int convertToPigLatin(char arr[50]) {
// Make sure newArr is properly terminated.
char newArr[50] = {0};
// [...]
} else { //else if the given input starts with a consonant
for (int r = 1; r < arrLength; r++) {
newArr[r-1] = newArr[r];
}
// Do this outside the loop.
newArr[arrLength-1] = lastChar;
// No need for strcat here.
cout << "Your word in piglatin is " << newArr << "ay" << endl;
}
}
return 0;
}
You need to add the '\0' at the end of newArr because strlen does not count it so you are not copying it. strcat replaces '\0' witn 'ay\0' but you have no '\0'.
for (int r = 1; r < arrLength; r++) {
newArr[r-1] = newArr[r];
newArr[arrLength] = lastChar;
}
newArr[arrLength+1] = '\0';
cout << "Your word in piglatin is " << strcat(newArr, "ay") << endl;

Switch statement : if input not a digit (1 to 9) or a letter (a to z) output << "error" ? C++

How can I output an "error" message if the input is not a letter from a to z or a digit from 1 to 9.
For exemple, if the user inputs '%', how can I output "error" ?
I tried this :
if ((name.letter <= a) && (name.letter >= z)) {
cout << "error";
}
but I guess it's just not C++ code...
There are useful functions in standard library cctype.
example:
#include <cctype>
if (!islower((unsigned char)name.letter) && (!isdigit((unsigned char)name.letter) || name.letter == '0')) {
cout << "error";
}
If the type of name is std::string, it may be like this:
#include <iostream>
#include <string>
#include <cctype>
using std::cout;
int main(void) {
std::string name = "1%";
// use loop to check every characters
bool valid = true;
for (size_t i = 0, len = name.length(); i < len; i++) {
unsigned char c = (unsigned char)name[i];
if (!islower(c) && (!isdigit(c) || name[i] == '0')) {
// illegal character is found
valid = false;
break;
}
}
if (!valid) {
cout << "error";
}
return 0;
}
You could easily check your conditions with a few things that already exist in the standard library (<cctype>). Namely:
isdigit - Checks whether a character is a decimal digit character.
isalpha - Checks whether a character is an alphabetic letter.
These both take int arguments, but you could do something like the following:
char c;
cin >> c;
if(!(isdigit(c) && c != '0') && !isalpha(c)) {
std::cerr << "Error!\n";
}
Note that isalpha will return true if c is a letter - this means uppercase or lowercase. If you really need one or the other, you could use islower or isupper.
Since name is a std::string, you could do the following:
for(auto c: name) {
if(!(isdigit(c) && c != '0') && !isalpha(c)) {
std::cerr << "Error: " << c << " is not a valid character\n";
break; // or return or exit
}
}
How can I output an "error" message
You have marked this post as C++.
One C++ way to handle this is:
void checkName(std::string name)
{
const std::string letters = "abcdefghijklmnopqrstuvwxyz";
const std::string digits = "123456789";
const std::string letterDigits (letters + digits);
std::cout << "\nname '" << name << "'" << std::endl;
size_t indx = name.find_first_not_of(letterDigits);
if(indx != std::string::npos)
{
std::cerr << "error at indx " << indx << " == '" << name[indx] << "'\n" << std::endl;
}
else
{
std::cout << "all chars of name '" << name << "' are letters or digits\n" << std::endl;
}
}
int main(int /*argc*/, char** /*argv*/ )
{
{
std::string name = "dan%iel";
checkName(name);
}
{
std::string name = "daniel";
checkName(name);
}
return (0);
}
Not with a switch statement! Just enclose the letters in single quotation marks, so they are treated as their ASCII values:
if ((name.letter <= 'a') && (name.letter >= 'z')) {
cout << "error";
}
Switch statements can only be used to match specific cases you specify, not ranges of cases as in some other languages.

Validating a password with c++ and giving specific errors

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