Im creating a password checker in C. The user enters their password and the program must check it fits the format aa$B1BB$B$$aB
(where a
represents lowercase, B
uppercase, $
symbol and 1
is a digit).
I have currently programmed an array of numbers 0
to 9
, symbols and letters and am trying to create a series of iterative loops to search each character of the password against the matching array of the criteria that it should meet. This was the easiest way I thought of but if anyone has any better suggestions it would be much appreciated.
The errors I am currently facing is that the program isn't running as I expected. If the password is not 13 characters long it should call the else if
statement but it doesn't. So far I have only created statements for checking digits 1
, 2
, 3
and 4
. The printf("Password ok #")
statements are just for testing purposes.
#include <string.h>
#include <stdio.h>
int main() {
char password[20];
int i, length;
int numbers[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
char letters[] = { 'a', 'b', 'c', 'c', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z' };
char symbols[] = { '!', '@', '#', '$', '%', '^', '&', '*',
'-', '_', ' ', '=' };
printf("Password rules:\n"
" --------------------- \n"
" must follow the format ee$L1LL$L$$eL \n"
" --------------------- \n"
" Please enter your current password:");
scanf("%c", password);
printf("s", password);
length = strlen(password);
if (length = 13) {
printf("Password ok 1");
for (int i = 0; i < strlen(letters); i ) {
if ((password[1] == letters[i]) && (password[2] == letters[i])) {
printf("Password ok 2");
} else {
printf("Digits 1 or 2 do not fit the correct format");
}
}
for (int i = 0; i < strlen(symbols); i ) {
if (password[3] == symbols[i]) {
printf("Password ok 3");
} else {
printf("Digit 3 does not fit the correct format");
}
}
for (int i = 0; i < strlen(letters); i ) {
if (password[4]==symbols[i]) {
printf("Password ok 4");
} else {
printf("Digit 3 does not fit the correct format");
}
}
} else if (length != 13) {
printf("\nPassword must be 13 digits long");
}
}
CodePudding user response:
To check if the password fits
format aa$B1BB$B$$aB
Use fgets()
to read, sscanf()
to scan and "%n"
to detect scanning end.
// scanf("%c",password); // %c only reads 1 character
// printf("s",password);
if (fgets(password, sizeof password, stdin)) {
password[strcspn(password, "\n")] = '\0'; // Lop off potential \n
printf("<%s>\n", password);
int n = 0;
// format aa$B1BB$B$$aB
#define FMT_a "%*1[a-z]"
#define FMT_B "%*1[A-Z]"
#define FMT_1 "%*1[0-9]"
#define FMT_S "%*1[-!@#$%^&*_ =]" // '-' first
sscanf(password,
FMT_a FMT_a FMT_S FMT_B FMT_1 FMT_B FMT_B FMT_S FMT_B FMT_S FMT_S FMT_a FMT_B "%n",
&n);
bool Success = n > 0 && password[n] == '\0';
A more robust approach uses a series of strchr()
calls in case letters [a-z] are not consecutive.
IMO, use a more generous read buffer.
// char password[20];
char password[100];
CodePudding user response:
If you're allowed to use ctype
macros like isupper()
, islower()
then you can call them against every pattern-char as.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
enum { // Enumerate codes used in the password-pattern
eLower = 'a',
eUpper = 'B',
eDigit = '1',
eSymbol = '$'
};
char* pp_errors[] = { // Error strings for pattern-mismatch
"No Error",
"Lower case letter expected",
"Upper case letter expected",
"Digit is expected",
"Punctuation expected",
"Pattern Not handled"
};
int
check_pattern_char (const unsigned char pwdC, const unsigned char patC)
{
switch (patC) {
case eLower : if (!islower (pwdC)) return 1;
break;
case eUpper : if (!isupper (pwdC)) return 2;
break;
case eDigit : if (!isdigit (pwdC)) return 3;
break;
case eSymbol : if (!ispunct (pwdC)) return 4;
break;
default: return 5; // pattern not handled
}
return 0;
}
int main ()
{
char password [128];
char pattern [] = "aa$B1BB$B$$aB";
int ptlen = strlen (pattern);
printf ("Enter a password that follows pattern [%s]: ", pattern);
while (1 != scanf ("7s", password));
if ( (int) strlen (password) != ptlen) {
printf ("\nPassword must be [%d] chars long.\n", ptlen);
return 1;
}
for (int pi = 0; pi < ptlen; pi) {
int status;
if ( (status = check_pattern_char (password[pi], pattern[pi]))) {
printf ("\nERROR: [%s] at pos[%d] [%c][%c]\n",
pp_errors[status], pi 1, pattern[pi], password[pi]);
return 2;
}
}
printf ("\nPassword [%s] accepted against Pattern[%s]\n", password, pattern);
return 0;
}
Or you can write your own functions for them like :
static inline int my_islower (const char ch) {
return (ch >= 'a' && ch <= 'z');
}
static inline int my_isupper (const char ch) {
return (ch >= 'A' && ch <= 'Z');
}
static inline int my_isdigit (const char ch) {
return (ch >= '0' && ch <= '9');
}
static inline int my_ispunct (const char ch) {
// allowed punctuations
char puncts[] = "!@#$%^&*-_ =";
for (char* pp = puncts; *pp; )
if (*pp == ch) return 1;
return 0;
}
static inline
is telling compiler that this function is local to this translation-unit
(current file) & requesting it to inline function if possible at the calling location. This mitigates the overheads of calling small functions.