-4

I want to verify if user's password is:

  1. Consists of 8 characters
  2. Consists of numbers and letters
  3. At least one big letter and one number
  4. It should contain $ or ! or ?
  5. The order doesn't matter

My code is:

r.match(r'([A-Z]+)([a-z])([0-9]+)($|?|!){8}',password)

Is it correct answer? I don't know how to specify, that order doesn't matter in regex.

EDITED Using some advices I have edited my code:

m = re.compile(r'^(?=.*\d)(?=.*[A-Z])(?=.*[\$|\?\!])[A-Za-z\d$!&]{8}$')
m.match('adwA12f!')
<_sre.SRE_Match object; span=(0, 8), match='adwA12f!'>

Can you please, if it is possible clarify, what exactly "?=." in regular expression means.

YNWA1992
  • 99
  • 1
  • 10
  • 1
    Have you tested it against any strings? Please add to the question. And the regex is not matching what you described. – Wiktor Stribiżew Jun 13 '16 at 16:37
  • A simple way to say that order doesn't matter is to individually test the patterns – OneCricketeer Jun 13 '16 at 16:38
  • I don't think trying to solve it in a single regular expression is a good idea. To improve readability and testability, solve it using the multiple checks, e.g. like was done [here](http://stackoverflow.com/a/32542964/771848). – alecxe Jun 13 '16 at 16:39
  • If all you have is a hammer, everything looks like a nail. Especially your thumb. – Karoly Horvath Jun 13 '16 at 16:40
  • 1
    I wonder if this is slower than a regex search:`len(s) == 8 and any(x.isdigit() for x in s) and any(x in ("$","!","?") for x in s) and any(x.isupper() for x in s)`. It *does* run through the string at least three times. – jDo Jun 13 '16 at 16:52
  • I checked and added the code. – YNWA1992 Jun 13 '16 at 18:26

3 Answers3

2

This seems to work tested in IDLE 2.7.9. Keep in mind this only matches with 8 characters

^(?=.*[!$?])(?=.*[a-z])(?=.*[A-Z]).{8}$

import re
p = re.compile('^(?=.*[!$?])(?=.*[a-z])(?=.*[A-Z]).{8}$')
test_str = 'a2Cd$F!8'
if re.search(p, test_str):
    print('string matched')

output:

string matched

?= is a lookahead assertion; suggest reading this site: "Lookahead Assertion"

. matches any character except a newline; since you have square brackets the . is limited to only what is defined in the []; also know as atomic grouping.

Chris
  • 934
  • 1
  • 17
  • 38
0

I guess the obvious non-regex way would be:

symbols = '$!?'
if (len(pwd) >= 8 and
        any(c.isdigit() for c in pwd) and
        any(c.isupper() for c in pwd) and
        any(c in symbols for c in pwd) and
        all(c.isalnum() or c in symbols for c in pwd)):
    print('good password')
else:
    print('bad password')

you didnt say it, but I assume you want at least one lowercase too. If so add this clause as well

any(c.islower() for c in pwd)
cmd
  • 5,754
  • 16
  • 30
0

At least on my system, doing the check without using re is a little faster. Also, the speed difference seems to get more pronounced as the password length increases.

import re
import timeit

passwords_varied = ("kk", "999999999999999999999999", "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk", "gkrDsh!2", "gjtlssssssssssssssssfsdg43ggw3fa2adada2fagaa2adajjjjjjjjjjjjjjjkhjkhjkhjkd45!", "kit43MM?", "fkthrej!", "483kkED!")

passwords_8chars = ("hktj33cD", "!?gk329s", "fkrK44?a", "dlekAS2$", "??ffD913")

def check1(s):
    return len(s) == 8 and \
    any(x.isdigit() for x in s) and \
    any(x in ("$","!","?") for x in s) and \
    any(x.isupper() for x in s)

def check2(s):
    if len(s) == 8 and \
    re.search(r"\d", s) and  \
    re.search(r"[$!?]", s) and \
    re.search(r"[A-Z]", s):
        return True
    return False

def run1(passwords):
    for password in passwords:
        check1(password) 

def run2(passwords):
    for password in passwords:
        check2(password)


def main():
    print ("---- list comprehension approach ----")
    print ("varying length passwords")
    print(timeit.timeit("run1(passwords_varied)", setup="from __main__ import run1, passwords_varied", number=100000))
    print ("correct length passwords")
    print(timeit.timeit("run1(passwords_8chars)", setup="from __main__ import run1, passwords_8chars", number=100000))
    print ""
    print "---- 're' approach ----"
    print ("varying length passwords")
    print(timeit.timeit("run2(passwords_varied)", setup="from __main__ import run2, passwords_varied", number=100000))
    print "correct length passwords"
    print(timeit.timeit("run2(passwords_8chars)", setup="from __main__ import run2, passwords_8chars", number=100000))


if __name__ == '__main__':
    main()


"""
---- list comprehension approach ----
varying length passwords
2.69666814804
correct length passwords
3.31486010551

---- 're' approach ----
varying length passwords
3.27806806564
correct length passwords
4.286420106

"""
jDo
  • 3,962
  • 1
  • 11
  • 30