4
votes

Compter le nombre de fois où plusieurs sous-chaînes apparaissent dans une chaîne à la fois

Je crée un script simple en Python qui évalue la force d'un mot de passe sur un système de score qui donne et prend des points selon qu'il contient ou non des lettres majuscules ou minuscules, des chiffres et des symboles pour l'école.

L'un des les exigences sont qu'il vérifie 3 lettres ou chiffres consécutifs de gauche à droite sur un clavier QWERTY britannique et enlève 5 points pour chaque instance. Par exemple, le mot de passe «qwer123» perdrait 15 points pour «qwe», «wer» et «123». Comment cela pourrait-il être accompli? Mon code actuel ci-dessous.

def check():
  user_password_score=0
  password_capitals=False
  password_lowers=False
  password_numbers=False
  password_symbols=False
  password_explanation_check=False
  ascii_codes=[]
  password_explanation=[]
  print("The only characters allowed in the passwords are upper and lower case letters, numbers and these symbols; !, $, %, ^, &, *, (, ), _, -, = and +.\n")
  user_password=str(input("Enter the password you would like to get checked: "))
  print("")
  if len(user_password)>24 or len(user_password)<8:
    print("That password is not between 8 and 24 characters and so the Password Checker can't evaluate it.")
    menu()
  for i in user_password:
    ascii_code=ord(i)
    #print(ascii_code)
    ascii_codes.append(ascii_code)
  #print(ascii_codes)
  for i in range(len(ascii_codes)):
    if ascii_codes[i]>64 and ascii_codes[i]<90:
      password_capitals=True
    elif ascii_codes[i]>96 and ascii_codes[i]<123:
      password_lowers=True
    elif ascii_codes[i]>47 and ascii_codes[i]<58:
      password_numbers=True
    elif ascii_codes[i] in (33,36,37,94,38,42,40,41,45,95,61,43):
      password_symbols=True
    else:
      print("Your password contains characters that aren't allowed.\n")
      menu()
  if password_capitals==True:
    user_password_score+=5
  if password_lowers==True:
    user_password_score+=5
  if password_numbers==True:
    user_password_score+=5
  if password_symbols==True:
    user_password_score+=5
  if password_capitals==True and password_lowers==True and password_numbers==True and password_symbols==True:
    user_password_score+=10
  if password_numbers==False and password_symbols==False:
    user_password_score-=5
  if password_capitals==False and password_lowers==False and password_symbols==False:
    user_password_score-=5
  if password_capitals==False and password_lowers==False and password_numbers==False:
    user_password_score-=5
  #print(user_password_score)
  if user_password_score>20:
    print("Your password is strong.\n")
  else:
    print("That password is weak.\n")
  #don't forget you still need to add the thing that checks for 'qwe' and other stuff.
  menu()


3 commentaires

Bienvenue dans Stack Overflow. Veuillez lire Comment demander et voir si vous pouvez rendre votre extrait de code un peu plus concis avec les parties essentielles .


Vous pouvez également publier votre code sur codereview.stackexchange.com . Si x est un booléen, vous pouvez remplacer if x == True par if x et if x == False avec sinon x .


Vous pouvez également écrire contains_numbers = any (char.isdigit () for char in password) ou contains_uppercase = any (char.isupper () for char in password) .


4 Réponses :


1
votes

Vous pouvez stocker des séquences interdites dans un ensemble de chaînes et décrémenter le score chaque fois que quelqu'un utilise cette séquence.

password = "qwert123"
score = 42          # initial score
sequences = {       # all in lowercase because of the `lower()` in the loop
    "qwertyuiopasdfghjklzxcvbnm",
    "azertyuiopqsdfghjklmwxcvbn",
    "abcdefghijklmnopqrstuvwxyz",
    "01234567890"
}
match_length = 3                        # length threshold for the sanction
sequences.update({s[::-1] for s in sequences})      # do we allow reverse ?

for c in range(len(password)-match_length+1):
    for seq in sequences:
        if password[c:c+match_length].lower() in seq:
            score-=5
            print(f"'{password[c:c+match_length]}' => -5 !")
            break   # Don't flag the same letters more than once

print(score) # 22 (42-4*5)


0 commentaires

0
votes

Le moyen le plus simple est de forcer brutalement toutes les séquences possibles.

Créez 4 chaînes: "1234567890" , "qwertyuiop" , "asdfghjkl ", " zxcvbnm " et parcourez chacun avec 3 caractères du user_password.

Vous pouvez initialiser cette liste au début de la fonction check :

if(i<len(ascii_codes)-2):  # since we will be checking for characters up to i+2 in our loop
    flag = False  # initialize a flag to signal finding a match
    for s in sequences:  # loop through each of the 4 keyboard sequences
        if(s.find(user_password[i: i+3].lower()) != -1): 
            user_password_score -= 5
            flag = True
            break 
        if(flag): break

puis à l'intérieur de la boucle for i in range (len (ascii_codes)) ajouter:

sequences = ["1234567890", "qwertyuiop", "asdfghjkl", "zxcvbnm"]


4 commentaires

notez également que la boucle se rompra après la première occurrence


@ BenoîtPilatte et c'est tout ce dont nous avons besoin, 3 personnages n'apparaîtront qu'une seule fois et dans une de ces séquences seulement!


@ BenoîtPilatte, c'est vrai, testez le code et vous verrez qu'il décrémente bien le score deux fois.


Ok, j'ai été confus par vos break et votre boucle.



0
votes

Je créerais une liste de séquences de touches adjacentes, comme d'autres mentionnées ci-dessus. Ensuite, je créerais une fonction de fenêtre coulissante pour générer toutes les séquences de longueur 3 et faire correspondre chacune d'elles avec le mot de passe: < pré> XXX


0 commentaires

0
votes

Si les expressions régulières sont autorisées, vous pouvez le faire en une seule ligne :

User password score: 42
Plain list of triplets: ['qwe', 'wer', 'ert', 'rty', 'tyu', 'yui', 'uio', 'iop', 'opa', 'pas', 'asd', 'sdf', 'dfg', 'fgh', 'ghj', 'hjk', 'jkl', 'klz', 'lzx', 'zxc', 'xcv', 'cvb', 'vbn', 'bnm', 'aze', 'zer', 'ert', 'rty', 'tyu', 'yui', 'uio', 'iop', 'opq', 'pqs', 'qsd', 'sdf', 'dfg', 'fgh', 'ghj', 'hjk', 'jkl', 'klm', 'lmw', 'mwx', 'wxc', 'xcv', 'cvb', 'vbn', 'abc', 'bcd', 'cde', 'def', 'efg', 'fgh', 'ghi', 'hij', 'ijk', 'jkl', 'klm', 'lmn', 'mno', 'nop', 'opq', 'pqr', 'qrs', 'rst', 'stu', 'tuv', 'uvw', 'vwx', 'wxy', 'xyz', '012', '123', '234', '345', '456', '567', '678', '789', '890']
Regex expression: (?=((qwe)|(wer)|(ert)|(rty)|(tyu)|(yui)|(uio)|(iop)|(opa)|(pas)|(asd)|(sdf)|(dfg)|(fgh)|(ghj)|(hjk)|(jkl)|(klz)|(lzx)|(zxc)|(xcv)|(cvb)|(vbn)|(bnm)|(aze)|(zer)|(ert)|(rty)|(tyu)|(yui)|(uio)|(iop)|(opq)|(pqs)|(qsd)|(sdf)|(dfg)|(fgh)|(ghj)|(hjk)|(jkl)|(klm)|(lmw)|(mwx)|(wxc)|(xcv)|(cvb)|(vbn)|(abc)|(bcd)|(cde)|(def)|(efg)|(fgh)|(ghi)|(hij)|(ijk)|(jkl)|(klm)|(lmn)|(mno)|(nop)|(opq)|(pqr)|(qrs)|(rst)|(stu)|(tuv)|(uvw)|(vwx)|(wxy)|(xyz)|(012)|(123)|(234)|(345)|(456)|(567)|(678)|(789)|(890)))
Matches : ['qwe', 'wer', '123']
Penalty: -15
Final score: 27

Ce code est équivalent:

import re

def build_pattern(sequences):
    all_triplets = []
    triplets = []
    for seq in sequences:
        for i in range(0, len(seq) - 2):
            triplets.append(seq[i:i+3])
        all_triplets.append(triplets)
        triplets = []
    expanded_triplets = [ x for y in all_triplets for x in y ]
    print("Plain list of triplets: " + str(expanded_triplets))
    string_pattern = '|'.join( [ "({0})".format(x) for x in expanded_triplets ] )
    lookahead_pattern = '(?=({0}))'.format(string_pattern)
    print("Regex expression: " + lookahead_pattern)
    return re.compile(lookahead_pattern)

password = 'qwer123'
user_password_score = 42
print("User password score: " + str(user_password_score))
sequences = ["qwertyuiopasdfghjklzxcvbnm", 
             "azertyuiopqsdfghjklmwxcvbn", 
             "abcdefghijklmnopqrstuvwxyz", 
             "01234567890"]
pattern = build_pattern(sequences)
matches = [ match.group(1) for match in pattern.finditer(password) ]
print("Matches : " + str(matches))
matches_count = len(matches)
penalty = -5 * matches_count
print("Penalty: " + str(penalty))
user_password_score += penalty
print("Final score: " + str(user_password_score))

Et le code suivant est également équivalent (et, espérons-le, également lisible par l'homme). Nous l'imprimerons étape par étape pour mieux voir ce qu'il fait.

import re
user_password_score = 42
pwd = 'qwer123'
seqs = ["qwertyuiopasdfghjklzxcvbnm", "azertyuiopqsdfghjklmwxcvbn", "abcdefghijklmnopqrstuvwxyz", "01234567890"]
pattern = re.compile('(?=({0}))'.format('|'.join(["({0})".format(w) for w in [x for y in [[s[i:i+3] for i in range(0,len(s)-2)] for s in seqs] for x in y]])))
penalty = -5 * len([match.group(1) for match in pattern.finditer(pwd) ])
user_password_score += penalty

Voici la sortie:

import re
user_password_score = 42
pwd = 'qwer123'
user_password_score += (lambda z : -5 * len([match.group(1) for match in re.compile('(?=({0}))'.format('|'.join(["({0})".format(w) for w in [x for y in [[s[i:i+3] for i in range(0,len(s)-2)] for s in ["qwertyuiopasdfghjklzxcvbnm", "azertyuiopqsdfghjklmwxcvbn", "abcdefghijklmnopqrstuvwxyz", "01234567890"]] for x in y]]))).finditer(z) ]))(pwd)

Dans la fonction build_pattern , [x for y in all_triplets for x in y] est une astuce pour développer une liste de listes en une liste simple. Un modèle d'expression régulière comme (lmw) | (mwx) | (wxc) utilisé dans un finditer () indique que nous voulons trouver toutes les correspondances pour lmw, mwx et wxc. Et lorsque nous enveloppons ce modèle dans une anticipation ( (? = ()) ), nous disons à re qu'il devrait également inclure des correspondances qui se chevauchent sur le résultat. P >


0 commentaires