Python 3 Vigenere Şifresi
Python'da hala oldukça yeniyim ve modülleri, işlevleri vb. Verimli bir şekilde kullanıp kullanmadığımı veya bir şeyler yapmanın başka / daha kolay bir yolu olup olmadığını görmeye çalışıyorum.
Bu Python 3 Vigenere Cipher, JavaScript tabanlı bir şifrenin yeniden oluşturulmasıdır ve Windows tabanlı. Herhangi bir alfabe karakterini kabul eder ve yerleşik demo seçeneklerine sahiptir. Cipher demosu CIA Kryptos şifresinin 1. aşamasını kullanır .
#! 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()
Yanıtlar
shebang
Mesele genel olmalıdır. Şu anda arama yapıyorsunuz pythonve bu, belirli sistemlerde python 2'ye işaret edebilir.
Genel, sanal çevre dostu bir python shebang:
#!/usr/bin/env python3
BEP-8
PEP-8 kılavuzundan birkaç nokta :
- Üst düzey işlevi ve sınıf tanımlarını iki boş satırla çevreleyin.
- Mantıksal bölümleri belirtmek için işlevlerde az miktarda boş satırlar kullanın.
- Sabitler genellikle bir modül düzeyinde tanımlanır ve sözcükleri ayıran alt çizgilerle tüm büyük harflerle yazılır.
BEP-484
Yazım ipucu işlevleri, işlevlerinizi takip etmenizi kolaylaştırır. Check PEP-484 .
if __name__ blok
Komut dosyanızın yürütme mantığını if __name__ == "__main__"bloğun içine koyun . Stack Overflow'da daha açıklayıcı bir açıklama kontrol edilebilir .
Gereksiz mantık
Kodunuzda, yalnızca kullanıcı girişini okumak için 5 farklı işleve sahipsiniz. Hepsi aynı işi yapıyor:
- sonsuz döngü
- kullanıcı girişi isteyin
- büyük harfe dönüştür
- girişin boş olup olmadığını (veya bir dizi geçerli değerde) doğrulayın
- boş değer olması durumunda, bir varsayılan döndür
- değerle geri dönmek
Tüm bunlar tek bir işlevle halledilebilir:
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
Normal ifade / doğrulama
Girdiyi doğrulamak için normal ifade izin verir _, oysa hata mesajı açıkça hiçbir özel karakter belirtmez. Yukarıdaki yeniden yazmadaki kontrol şu şekilde yapılır (aşağıdaki yoruma göre güncellenir):
value.isalpha() and value.isascii()
bu, regex'ten daha hızlı performans gösterir (kullanıcı sürekli yanlış değerler girmedikçe \$ 10^ n \$önceden derlenmiş desenin biraz daha iyi performans gösterebileceği zamanlar ).
Verim
Kodu daha performanslı hale getirmek için değiştirilebilecek birkaç şey:
Dizeye birleştirmek (eklemek) yerine,
mode_stringbir listeye itin ve sonunda kullanın"".join(). Stack Overflow hakkında daha fazla ayrıntı .Programınızın linux (* nix) sistemlerini de desteklemesini sağlayabilirsiniz. Windows'taki tek bağımlılık, sistem çağrınızdır
cls. Belki ( Stack Overflow'dan alınmıştır ):def clear(): os.system("cls" if os.name == "nt" else "clear")Çok benzer adlara sahip 2 işlev vardır:
start_cipher(mode...)vestart_cipher_mode(mode). Bu, hangisinin gerçekten şifreyi başlattığını bilmeyi gerçekten zorlaştırır . Belki 2 ayrı işlevi vardırencryptvedecrypt?Modulo işlemini kullanarak aşağıdaki koşulu kaldırabilirsiniz:
if cipher_alphabet_index == len(cipher_alphabets): cipher_alphabet_index = 0ve şöyle görünür:
result.append(alphabet[cipher_alphabets[cipher_alphabet_index % alphabets_length].find(char)]Dizeyi yalnızca
alphabetiçindeki karakterlerin dizin değerleriyle gerçekten çalışmak için kullandığınızdan, bir sözlük yapın. Sözlükteki aramalar \$ O(1) \$\ ile karşılaştırıldığında$ O(n) \$için.find(). Bu olabilir:from itertools import count alphabet_map = dict(zip(alphabet, count()))Yukarıdaki 2 noktadan, kullanıcı girişinden sonra gerçekten karakterlere / alfabelere ihtiyacınız olmadığı açıktır. Yalnızca indeks değeri modülü önemlidir. Önemli bir matematiksel anlayış olmadan bunu anlamak / uygulamak zor olabilir, bu yüzden bunu şimdilik atlayabilirsiniz.
@hjpotter yorumlarımın çoğunu kapladı.
Boole'lar
Python, doğru ve yanlış değerler kavramına sahiptir , bu nedenle değerleri Doğru veya Yanlış ile karşılaştırmak yerine sadece doğrudan boole olarak ele almak tercih edilir.
return True if re.match("^[a-zA-Z_]*$", input_string) else False
basitleştirilebilir:
return re.match("^[a-zA-Z_]*$", input_string)
elif string_is_alpha(alphabet) is False:
Bu, şu şekilde basitleştirilebilir:
elif not string_is_alpha(alphabet):
Genel olarak, karşılaştırma için nadiren "is" kullanmak istersiniz. (Ana istisna, karşılaştırmadır None.)
Normal ifadeleri derleme
Bu neredeyse kesinlikle gereksiz bir performans iyileştirmesidir, ancak daha sonra bilmek faydalı olabilir:
Çağrının, re.matchher çağrıldığında regexp'i derlemesi gerekir. Düzenli ifadeyi bir kez önceden derleyebilir ve ardından matchhızlandırmak için derlenmiş nesneyi çağırabilirsiniz .
Küresel
globalAnahtar kelimeye neredeyse her ulaştığımda , bunun bir hata olduğu ortaya çıkıyor.
Demo tanımlayıcıları global olarak belirtmeniz gerektiğini düşünmüyorum; halihazırda kullanıma hazır olmaları gerekir (sadece okumak için - eğer onlara yazmaya çalışırsanız, yeni kapsamda orijinalleri gizleyerek yeni bir değişken tanımlayacaksınız).