简体   繁体   中英

checking if certain characters are in a string in C

Hi I'm a newbie C learner and this is an assignment.

The task is to write a program that changes a user's password and check if the new given password includes at least a number, a letter and one of these ( @ , # , $ , % ) signs and its length should be between 6 and 20.

I've described 3 variables as counter1 , counter2 & counter3 to check if at least a sign, a number & a letter is in the given password and if one of them was 0 scan another password from the user. This should continue until an entered password includes all the terms above.

My program will check the numbers, letters & the signs in given password but as the first new password is entered the program will end whether it contains all the terms or not, this is the part of the program that I assume the problem is from

int counter1 = 0, counter2 = 0, counter3 = 0;
char mypassword[20] = "A1$B2C";
char password[20];
char newpassword[50];
int check = 0;
do {
    printf("Please enter your current password: ");
    scanf("%s", password);
} while (strcmp(mypassword, password) != 0);
printf("Please enter your new password: ");
scanf("%s", newpassword);

for (int i = 0; i < strlen(newpassword); i++) {
    if (isalpha(newpassword[i]) != 0) {
        counter3 += 1;
    }
    else if (isdigit(newpassword[i]) != 0) {
        counter2 += 1;
    }
    else if (newpassword[i] == '@' || '#' || '$' || '%') {
        counter1 += 1;
    }
}
while (check == 0) {
    while (counter1 == 0 || counter2 == 0 || counter3 == 0) {
        printf("Please enter you new password: ");
        scanf("%s", newpassword);
    }
    if (strlen(newpassword) < 6 || strlen(newpassword) > 20) {
        printf("Please enter you new password: ");
        scanf("%s", newpassword);
    }
    else
        check += 1;
}

The test for special characters is incorrect: if (newpassword[i] == '@' || '#' || '$' || '%') will always succeed because '#' taken as a boolean is true.

You should instead write:

int c = newpassword[i];
if (c == '@' || c == '#' || c == '$' || c == '%') {
    counter1++;
}

There are other problems in your code:

  • you do not pass a maximum number of characters for scanf() to read into password : any input word longer than 19 bytes will cause undefined behavior.

  • you do not test for scanf() failure: unexpected end of file will cause undefined behavior and likely an endless loop.

  • you should be more explicit about the reason for prompting for the new password again.

  • the counters should be reset to 0 for each new password entered and their names should be more meaningful.

  • all tests must be tried again for every new attempt: Use a for loop with a single instance of scanf() to read the current or new password, perform the tests and exit the loop if all tests succeed. As a rule of thumb, avoid do / while loops as they tend to induce confusing and redundant code.

Here is a modified version:

#include <ctype.h>
#include <stdio.h>
#include <string.h>

int get_new_password(char *output,              // destination array, longer than max_length
                     int min_length,            // minimum password length
                     int max_length,            // maximum password length
                     const char *special_chars, // set of special chars if any required
                     const char *current_pwd)   // current password if any
{
    char password[50];
    char newpassword[50];
    int has_letter, has_digit, has_special, len, c;

    if (current_pwd != NULL && *current_pwd != '\0') {
        for (;;) {
            printf("Please enter your current password: ");
            if (scanf("%49s", password) != 1)
                return -1;
            if (strcmp(current_pwd, password) != 0) {
                printf("Invalid password. Try again.\n");
                continue;
            }
            break;  // current password is correct.
        }
    }

    for (;;) {
        printf("Please enter your new password: ");
        if (scanf("%49s", newpassword) != 1)
            return -1;

        len = strlen(newpassword);
        if (len < min_length) {
            printf("The password must have at least %d characters.\n", min_length);
            continue;
        }
        if (len > max_length) {
            printf("The password must have at most %d characters.\n", max_length);
            continue;
        }
        has_letter = has_digit = has_special = 0;

        for (int i = 0; i < len; i++) {
            c = newpassword[i];
            if (isalpha((unsigned char)c)) {
                has_letter = 1;
            } else
            if (isdigit((unsigned char)c)) {
                has_digit = 1;
            } else
            if (special_chars && strchr(special_chars, c) != NULL) {
                has_special = 1;
            }
        }
        if (has_letter == 0) {
            printf("The password must have at least one letter.\n");
            continue;
        }
        if (has_digit == 0) {
            printf("The password must have at least one digit.\n");
            continue;
        }
        if (special_chars != NULL && has_special == 0) {
            printf("The password must have at least one of %s\n", special_chars);
            continue;
        }
        break;  // new password passed all tests
    }
    strcpy(output, newpassword);
    return 0;
}

int main() {
    char mypassword[] = "A1$B2C";
    char newpassword[21];

    if (get_new_password(newpassword, 6, 20, "@#$%", mypassword)) {
        printf("Invalid input, aborted.\n");
        return 1;
    } else {
        printf("New password: %s\n", newpassword);
        return 0;
    }
}

I suggest you would break your validation into a separate function, makes the code more readable. Below is a suggestion, which also lets you easily print out which validation criteria fails in case of an invalid password.

#define VALIDATE_OK 0
#define VALIDATE_TOO_SHORT (1 << 0)
#define VALIDATE_TOO_LONG (1 << 1)
#define VALIDATE_LETTER (1 << 2)
#define VALIDATE_DIGIT (1 << 3)
#define VALIDATE_SPECIAL_CHAR (1 << 4)

#define MIN_LENGTH 6
#define MAX_LENGTH 20

static int validate(const char *str) {
    const char special_chars[] = { '@', '#', '$', '%' };
    size_t len = strlen(str);
    int ret = VALIDATE_TOO_SHORT|VALIDATE_TOO_LONG|VALIDATE_LETTER|
        VALIDATE_DIGIT|VALIDATE_SPECIAL_CHAR;

    if (len >= MIN_LENGTH) ret &= ~VALIDATE_TOO_SHORT;
    if (len <= MAX_LENGTH) ret &= ~VALIDATE_TOO_LONG;
    for (size_t i = 0; i < len; i++) {
        if (isalpha((unsigned char)str[i])) {
             ret &= ~VALIDATE_LETTER;
             continue;
        }
        if (isdigit((unsigned char)str[i])) {
            ret &= ~VALIDATE_DIGIT;
            continue;
        }
        for (size_t j = 0; j < sizeof special_chars; j++) {
            if (str[i] == special_chars[j]) {
                ret &= ~VALIDATE_SPECIAL_CHAR;
                break;
            }
        }
    }
    return ret;
}

EDIT: Now properly returns all missing criteria.

Use strchr ( https://www.cplusplus.com/reference/cstring/strchr/ ). If the char wasn't found, it returns a null pointer.

Edit: Another solution would be to use a for loop:

size_t len=strlen(newpassword);
if(len<3||len>20){
  //Invalid length
}
int hasDigit=0;
int hasSpecial=0;
int hasLetter=0;
for(size_t i=0;i<len;i++) {
  char c=newpassword[i];
  if(isdigit(c)){
    hasDigit=1;
  }else if(c=='@'||c=='#'||c=='$'||c=='%'){
    hasSpecial=1;
  }else{
    hasLetter=1;
  }
  ...
}
if(hasDigit&&hasSpecial&&hasLetter){
  //Valid password
}

Edit 2:

if (isalpha(newpassword[i]) != 0){
        counter3 += 1;
    }

I think this shouldn't be there and simply in the else clause:

else{
   counter3 += 1;
}

(This will be too long for a comment) Lets start at the beginning:

  1. You ask the user for the current password. 1.1 Repeat, if it's the wrong one.
  2. You ask for the new one.
  3. You go about every char in the new password.
    3.1 If it is a digit, add 1 to counter 2
    3.2 If it is a special char, add 1 to counter 1
    3.3 Here you should simply add 1 to counter1.
    4.After that you enter the while(check == 0) -Loop
    4.1 You enter the while(counter1 == 0 || counter2 == 0 || counter3 == 0) loop, these will be endless, as it is impossible to changle one of the counter variables.
    4.2 Everything else is dead code, because it can't be accessed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM