-2

i have problem with writing regex pattern for following statements. i think i have problem with pattern of email.

for email:

  1. domain should be end with google.com or (nothing or any characters).go
  2. at least we should have 3 numbers and all letters should be lowercase
  3. in email address we just have one $

for password:

  1. at least it should be 8 characters
  2. it should has at least one Uppercase letter and at least one number
  3. it should has at least one of these characters !@#$%^
email_pattern = re.compile(r'^((?=.*\$)((?=.*[0-9]){3,})(?=.*[a-z]*))(\@)((google\.com)|([a-z0-9\!\@\#\%\^\&]*\.go))$')

pass_pattern = re.compile(r'((?=.*(\!|\@|\#|\$|\%|\^))(?=.*[A-Z])(?=.*[0-9])(?=.*[a-z])(?=.*[a-zA-z0-9!@#$%^]*)){8,}')
  • 4
    Why should the email address have `$`? Do you mean `@`? – Barmar Aug 18 '23 at 21:26
  • This doesn't strike me as a regular expression problem at all. It's a parsing problem. Split at @ to check the two halves separately. Use `'$' in username` to check for that. Scan the username part one letter at a time to check for case and to count digits – Tim Roberts Aug 18 '23 at 22:15
  • Instead of checking passwords for "length X and characters ....", check them for strength, and don't do that yourself. Use a library that can determine how long it would take someone to crack it. Anything less than 5 years is a bad password. Your database doesn't care, don't use rules that make for bad passwords, _test the password_ and reject it if it's too weak. Then save the (salted) hash of that into your database, because you literally don't care about the password itself, all you need to save is the hash. You _never_ save the actual password. – Mike 'Pomax' Kamermans Aug 18 '23 at 22:31

1 Answers1

1

In my opinion, there is no need to use this kind of regular expression. They are scary, difficult to understand and difficult to customize. If you need to validate email and password, then why not break it down into several steps? Yes, there will probably be more code, but further interaction with it will become easier. If necessary, you can remove one of the checks or add a new one without touching the others. Read something about the principle of "Divide and Conquer".

Here is one of the easily customizable and readable solutions to your problem:

import re


def email_is_valid(email:str) -> bool:
    ''' Returns True if email is valid and False if it's not
    '''
    conditions = [
        # Checking for correct ending and forbidden characters
        re.fullmatch(r'[a-z0-9$]{1,}((@google.com)|(\.go))$', email),
        # Checking for at least three digits
        re.search(r'(?=(?:.*\d){3,})', email),
        # Checking for only one '$'
        email.count('$') == 1,
    ]
    for condition in conditions:
        if not condition:
            return False
    return True


def password_is_valid(password:str) -> bool:
    ''' Returns True if password is valid and False if it's not
    '''
    conditions = [
        # Checking the length and presence of forbidden characters
        re.fullmatch(r'[A-Za-z0-9!@#$%^]{8,}', password),
        # Looking for at least one of these characters !@#$%^
        re.search(r'[!@#$%^]{1,}', password), 
        # Looking for at least one Uppercase letter
        re.search(r'[A-Z]{1,}', password),
    ]
    for condition in conditions:
        if not condition:
            return False
    return True

And here is how the use of these functions will look like. Easy to read.

import email_is_valid, password_id_valid


if email_is_valid(your_email):
    ...

if password_id_valid(your_password):
    ...
  • In general, I still do not understand what $ does in e-mail :D – lengthylyova Aug 19 '23 at 00:44
  • Besides we don't know why OP wants a dollar `$` (we all want dollars of course) :D just to mention your regex [`(?=(?:.*\$){1})`](https://regex101.com/r/xU9Zxk/1) does not check for just one dollar sign. It looks at each position if there is a dollar ahead. Further the `{1}` is redundant, you could as well just search for `\$`. To validate there is only one dollar in the string you can anchor the string and use a [negated class](https://www.regular-expressions.info/charclass.html#negated) before and after it: [`^[^$]*\$[^$]*$`](https://regex101.com/r/hEsxBJ/1) – bobble bubble Aug 19 '23 at 11:29
  • But if you already argue against use of regex this condition could just be checked by [counting dollars](https://tio.run/##K6gsycjPM/7/v6AoM69EQ91TISOxLFVBRU9FXS85vxQkpKKuqfn/PwA) couldn't it. In general I agree that breaking it down into smaller steps is useful when validating multiple conditions, also to track which condition failed. – bobble bubble Aug 19 '23 at 11:32
  • 1
    @bobblebubble Yeah, just counting dollars is a more understandable way to check this kind of condition. The question was about regex, so i decided to use this (and as you said i made a mistake). I’ll update the solution right now. – lengthylyova Aug 19 '23 at 21:33