Chiffre Python 3 Vigenere
Je suis encore assez nouveau dans Python et j'essaie de voir si j'ai utilisé efficacement des modules, des fonctions, etc. ou s'il existe un autre moyen / plus simple de faire quelque chose.
Ce chiffrement Python 3 Vigenere est une reconstruction d'un chiffrement basé sur JavaScript et basé sur Windows. Il accepte tous les caractères de l'alphabet et possède des options de démonstration intégrées. La démo de chiffrement utilise l'étape 1 du chiffrement CIA Kryptos .
#! python
import os
import re
## Initialize global variables
continue_cipher = ""
demo_alphabet = "KRYPTOSABCDEFGHIJLMNQUVWXZ"
demo_key = "PALIMPSEST"
demo_cipher_string = "EMUFPHZLRFAXYUSDJKZLDKRNSHGNFIVJYQTQUXQBQVYUVLLTREVJYQTMKYRDMFD"
demo_cipher_decoded = "BETWEENSUBTLESHADINGANDTHEABSENCEOFLIGHTLIESTHENUANCEOFIQLUSION"
## Visuals
def display_header():
print("################################################")
print("# #")
print("# --- VIGENERE CIPHER --- #")
print("# #")
print("# A simple Vigenere cipher decoder/encoder #")
print("# #")
print("################################################", end="\n\n")
return
def display_results(mode, cipher_vars):
# Clear screen for final results
os.system('cls')
# Display header
display_header()
# Decompose cipher_vars
(alphabet, key, cipher_string, results) = cipher_vars
print("Mode:", "Decrypt" if mode == "D" else "Encrypt", end="\n\n")
print("Alphabet:", alphabet)
print("Key:", key)
print("Cipher String:", cipher_string, end="\n\n")
print("Decoded string:" if mode == "D" else "Encoded string:", results, end="\n\n")
return
## Validations
def string_is_alpha(input_string):
return True if re.match("^[a-zA-Z_]*$", input_string) else False
## Cipher variables
def get_alphabet():
global demo_alphabet
while True:
alphabet = input("Enter cipher alphabet: ").upper()
if alphabet == "":
alphabet = demo_alphabet
break
elif string_is_alpha(alphabet) is False:
print("The alphabet is not valid. Alphabet should not contain spaces, digits or special characters.")
else:
break
return alphabet
def get_key():
global demo_key
while True:
key = input("Enter cipher key: ").upper()
if key == "":
key = demo_key
break
elif string_is_alpha(key) is False:
print("The key is not valid. Key should not contain spaces, digits or special characters.")
else:
break
return key
def get_cipher_string(mode):
global demo_cipher_string
global demo_cipher_decoded
while True:
cipher_string = input("Enter cipher string: ").upper()
if cipher_string == "":
cipher_string = demo_cipher_string if mode == "D" else demo_cipher_decoded
break
elif string_is_alpha(cipher_string) is False:
print("The cipher string is not valid. Cipher strings should not contain spaces, digits or special characters.")
else:
break
return cipher_string
## Cipher actions
def get_cipher_alphabets(alphabet, key):
cipher_alphabets = []
for char in key:
char_index = alphabet.find(char)
cipher_alphabet = alphabet[char_index:] + alphabet[:char_index]
cipher_alphabets.append(cipher_alphabet)
return cipher_alphabets
def start_cipher(mode, alphabet, key, cipher_string):
mode_string = ""
cipher_alphabets = get_cipher_alphabets(alphabet, key)
cipher_alphabet_index = 0
for char in cipher_string:
# Reset cipher_alphabet_index to 0 when at end of cipher alphabets
if cipher_alphabet_index == len(cipher_alphabets):
cipher_alphabet_index = 0
# Use appropriate alphabet based on mode
# Syntax: base_alphabet[mode_alphabet.find(char)]
if mode == "D":
mode_string += alphabet[cipher_alphabets[cipher_alphabet_index].find(char)]
else:
mode_string += cipher_alphabets[cipher_alphabet_index][alphabet.find(char)]
cipher_alphabet_index += 1
return mode_string
## Cipher Mode
def get_cipher_mode():
while True:
cipher_mode = input("Choose cipher mode - [D]ecrypt or [E]ncrypt: ").upper()
if cipher_mode != "D" and cipher_mode != "E":
print("That is not a valid option. Please enter 'D' for decrypt and 'E' for encrypt.")
else:
break
print("")
return cipher_mode
def start_cipher_mode(mode):
print("Press 'enter' to use demo options")
alphabet = get_alphabet()
key = get_key()
cipher_string = get_cipher_string(mode)
mode_string = start_cipher(mode, alphabet, key, cipher_string)
return alphabet, key, cipher_string, mode_string
## Loop cipher
def get_continue_cipher():
while True:
continue_cipher = input("Do you want to decode/encode more? [Y/N]: ").upper()
if continue_cipher != "Y" and continue_cipher != "N":
print("That is not a valid option. Please enter 'Y' to continue and 'N' to quit.")
else:
break
return continue_cipher
## Start vigenere cipher program
while continue_cipher != "N":
# Clear the screen after each operation
os.system('cls')
# Display header
display_header()
# Determine cipher mode
cipher_mode = get_cipher_mode()
cipher_vars = start_cipher_mode(cipher_mode)
# Display results
display_results(cipher_mode, cipher_vars)
continue_cipher = get_continue_cipher()
Réponses
case
Le shebang doit être générique. Vous appelez actuellement python, ce qui pourrait pointer vers python 2 sur certains systèmes.
Un shebang python générique et respectueux de l'environnement virtuel est:
#!/usr/bin/env python3
PEP-8
Quelques points du guide PEP-8 :
- Entourez les définitions de fonction et de classe de niveau supérieur avec deux lignes vides.
- Utilisez des lignes vides dans les fonctions, avec parcimonie, pour indiquer les sections logiques.
- Les constantes sont généralement définies au niveau du module et écrites en majuscules avec des traits de soulignement séparant les mots.
PEP-484
Les fonctions d'indication de type facilitent le suivi de vos fonctions. Découvrez le PEP-484 .
if __name__ bloquer
Mettez la logique d'exécution de votre script dans le if __name__ == "__main__"bloc. Une explication plus descriptive peut être vérifiée sur Stack Overflow .
Logique redondante
Dans votre code, vous avez 5 fonctions différentes, uniquement pour lire les entrées utilisateur. Tous ont le même travail:
- boucle infinie
- demander l'entrée de l'utilisateur
- convertir en majuscules
- valider si l'entrée est vide (ou dans un ensemble de valeurs valides)
- en cas de valeur vide, renvoie une valeur par défaut
- retour avec la valeur
Tout cela pourrait être géré par une seule fonction:
def ask_user_input(message: str, options: List[str] = None, default: str = None, check_alpha: bool = False) -> str:
if not any([options, default]):
raise ValueError("Either a set of `options` for validation or a fallback `default` needed.")
while True:
value = input(message).upper()
if options:
if value in options:
break
else:
print(f"Invalid value. Select one of {', '.join(options)}")
continue
if default is not None:
if not value:
value = default
break
elif not check_alpha:
break
elif not (value.isalpha() and value.isascii()):
print("The input text should only consist of ascii alphabets.")
continue
else:
break
return value
Regex / validation
L'expression régulière de validation de l'entrée le permet _, alors que le message d'erreur ne dit explicitement aucun caractère spécial. La vérification de la réécriture ci-dessus est effectuée en utilisant (mis à jour en fonction du commentaire ci-dessous):
value.isalpha() and value.isascii()
qui fonctionnera plus rapidement que regex (à moins que l'utilisateur continue de saisir de mauvaises valeurs \$ 10^ n \$fois, auquel le modèle précompilé peut fonctionner légèrement mieux).
Performance
Quelques éléments qui peuvent être modifiés pour rendre le code plus performant:
Au lieu de concaténer (ajouter) à une chaîne
mode_string, poussez vers une liste et à la fin, utilisez"".join(). Plus de détails sur Stack Overflow .Vous pouvez également faire en sorte que votre programme prenne en charge les systèmes Linux (* nix). La seule dépendance de Windows est votre appel système
cls. Peut-être ( tiré de Stack Overflow ):def clear(): os.system("cls" if os.name == "nt" else "clear")Il existe 2 fonctions avec des noms très similaires:
start_cipher(mode...)etstart_cipher_mode(mode). Cela rend vraiment difficile de savoir lequel commence vraiment le chiffrement . Peut-être, avoir 2 fonctions distinctesencryptetdecrypt?À l'aide de l'opération modulo, vous pouvez supprimer le conditionnel suivant:
if cipher_alphabet_index == len(cipher_alphabets): cipher_alphabet_index = 0et ressemblerait à:
result.append(alphabet[cipher_alphabets[cipher_alphabet_index % alphabets_length].find(char)]Comme vous n'utilisez la
alphabetchaîne que pour travailler avec les valeurs d'index des caractères qu'elle contient, créez un dictionnaire. Les recherches dans le dictionnaire sont \$ O(1) \$par rapport à \$ O(n) \$pour le.find(). Ce serait:from itertools import count alphabet_map = dict(zip(alphabet, count()))À partir des 2 points ci-dessus, il est clair que vous n'avez pas vraiment besoin de caractères / alphabets après la saisie de l'utilisateur. Seule la valeur d'index modulo compte. Cela peut être difficile à comprendre / implémenter sans une compréhension mathématique considérable, vous pouvez donc l'ignorer pour le moment.
@hjpotter a couvert la plupart de mes commentaires.
Booléens
Python a un concept de valeurs véridiques et fausses , il est donc préférable de traiter les valeurs comme des booléens directement, plutôt que de les comparer à True ou False.
return True if re.match("^[a-zA-Z_]*$", input_string) else False
peut être simplifié en:
return re.match("^[a-zA-Z_]*$", input_string)
elif string_is_alpha(alphabet) is False:
Cela peut être simplifié en:
elif not string_is_alpha(alphabet):
Si général, vous souhaitez rarement utiliser "est" pour la comparaison. (La principale exception est la comparaison avec None.)
Compilation d'expressions régulières
Il s'agit presque certainement d'une amélioration inutile des performances, mais il peut être utile de le savoir pour plus tard:
L'appel à re.matchdoit compiler l'expression rationnelle chaque fois qu'il est appelé. Vous pouvez précompiler l'expression rationnelle une fois, puis appeler matchl'objet compilé pour l'accélérer.
Globales
Presque chaque fois que j'atteins le globalmot - clé, cela s'avère être une erreur.
Je ne pense pas que vous ayez besoin de déclarer les identifiants de démonstration comme globaux; ils devraient déjà être disponibles pour être utilisés (en lecture seule - si vous essayez de leur écrire, vous définirez une nouvelle variable dans la nouvelle portée, masquant les originaux).