0

I would like to check if a string contains at least: 12 characters, min a letter, min a number and finally min a non-alphanumeric character. I am in the process of creating a Regex but it does not meet my expectations.

Here is the Regex:

regex = re.compile('([A-Za-z]+[0-9]+\W+){12,}')


def is_valid(string):
    return re.fullmatch(regex, string) is not None


test_string = "abdfjhfl58425!!"
print(is_valid(test_string))

When the string contains numbers after letters, it does not match!

Could you help me? Thank you.

bendour
  • 61
  • 5

4 Answers4

1

Your regex is wrong. I found this on another post which describes a different scenario albeit very similar.

You can tweak this regex so that it reads like this:

^(.{0,12}|[^a-zA-Z]{1,}|[^\d]{1,}|[^\W]{1,})$|[\s]

Now what you have here is a regex that matches only when the password is invalid. Meaning that if you have no matches, the password is valid, and if you have matches the password is invalid. So you will need to alter the code to suit but try that regex above instead and it should work for all combinations.

The final working code would then be (with extra tests):

import re

regex = re.compile('^(.{0,12}|[^a-zA-Z]{1,}|[^\d]{1,}|[^\W]{1,})$|[\s]')


def is_valid(string):
    return re.fullmatch(regex, string) is None


test_string = "abdfl58425B!!"
print(is_valid(test_string))

test_string = "ABRER58425B!!"
print(is_valid(test_string))

test_string = "eruaso58425!!"
print(is_valid(test_string))
Dharman
  • 30,962
  • 25
  • 85
  • 135
Johan Jarvi
  • 297
  • 6
  • 13
0

Regex is not really suited to this task as it involves remembering counts of each type of character. You could construct a regex to do it but it would end up being very long and unreadable. Much simpler to write a function to count the number of occurrences of each type of character, something like:

def is_valid(test_string):
    if len(test_string) >= 12 \
    and len([c for c in test_string if c.isalpha()]) >= 1 \
    and len([c for c in test_string if c.isnumeric()]) >= 1 \
    and len([c for c in test_string if not c.isalnum()]) >= 1:
        return True
    else:
        return False
ljdyer
  • 1,946
  • 1
  • 3
  • 11
  • Looping three times to do almost the same thing is inefficient, you could capture all the states with a single read ;) Also, you can stop as soon as you found **one*" match – mozway Dec 05 '21 at 21:13
0

If that helps: if you want to do the same thing but without ReGex, you can use this function that I had done! It works perfectly!

def is_strong_password(a_string):
    if len(a_string) >= 12:
        chiffre = 0
        lettre = 0
        alnum = 0
        for x in a_string:
            if x.isalpha():
                lettre += 1
            if x.isdigit():
                chiffre += 1
            if not x.isalnum():
                alnum += 1
        if lettre > 1 and chiffre > 1 and alnum > 1:
            return True
        else:
            return False
    else:
        return False
bendour
  • 61
  • 5
0

You could four positive lookaheads:

(?i)(?=.{12})(?=.*[a-z])(?=.*\d)(?=.*[^a-z\d])

Demo

(?i) specifies that matches are to be case-indifferent.

The four positive lookaheads are as follows:

(?=.{12})      # assert that the string contains (at least) 12 characters
(?=.*[a-z])    # assert that the string contains a letter
(?=.*\d)       # assert that the string contains a digit
(?=.*[^a-z\d]) # assert that the string contains a non-alphanumeric character
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100