Python 3 Vigenere Cipher
ฉันยังค่อนข้างใหม่สำหรับ Python และฉันกำลังพยายามดูว่าฉันใช้โมดูลฟังก์ชัน ฯลฯ อย่างมีประสิทธิภาพหรือไม่หรือมีวิธีอื่นที่ง่ายกว่าในการทำอะไรบางอย่าง
Python 3 Vigenere Cipher นี้สร้างขึ้นใหม่ของการเข้ารหัสที่ใช้ JavaScript และใช้ใน Windows ยอมรับอักขระตัวอักษรใด ๆ และมีตัวเลือกการสาธิตในตัวการสาธิตการเข้ารหัสใช้ขั้นตอนที่ 1 ของการเข้ารหัส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()
คำตอบ
Shebang
Shebang ควรเป็นแบบทั่วไป คุณกำลังโทรอยู่pythonซึ่งอาจชี้ไปที่ python 2 ในบางระบบ
Python Shebang ทั่วไปที่เป็นมิตรกับสิ่งแวดล้อมเสมือนคือ:
#!/usr/bin/env python3
PEP-8
ไม่กี่คะแนนจากคู่มือ PEP-8 :
- ฟังก์ชันระดับบนสุดรอบทิศทางและคำจำกัดความของคลาสด้วยบรรทัดว่างสองบรรทัด
- ใช้บรรทัดว่างในฟังก์ชันเท่าที่จำเป็นเพื่อระบุส่วนตรรกะ
- โดยปกติค่าคงที่จะถูกกำหนดในระดับโมดูลและเขียนด้วยตัวพิมพ์ใหญ่ทั้งหมดโดยมีเครื่องหมายขีดล่างคั่นคำ
PEP-484
ฟังก์ชันคำใบ้ประเภทช่วยให้ติดตามฟังก์ชั่นของคุณได้ง่ายขึ้น ตรวจสอบห้าวหาญ-484
if __name__ บล็อก
ใส่ตรรกะการดำเนินการของสคริปต์ของคุณภายในif __name__ == "__main__"บล็อก คำอธิบายอธิบายเพิ่มเติมสามารถตรวจสอบได้ที่กองมากเกิน
ตรรกะซ้ำซ้อน
ในโค้ดของคุณคุณมีฟังก์ชันที่แตกต่างกัน 5 ฟังก์ชันเพื่ออ่านข้อมูลของผู้ใช้เท่านั้น พวกเขาทั้งหมดมีงานเดียวกันคือ:
- วนไม่มีสิ้นสุด
- ขอข้อมูลผู้ใช้
- แปลงเป็นตัวพิมพ์ใหญ่
- ตรวจสอบว่าอินพุตว่างหรือไม่ (หรือในชุดของค่าที่ถูกต้อง)
- ในกรณีของค่าว่างให้ส่งคืนค่าเริ่มต้น
- ส่งคืนด้วยค่า
ทั้งหมดนี้สามารถจัดการได้โดยฟังก์ชั่นเดียว:
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 / การตรวจสอบความถูกต้อง
regex สำหรับการตรวจสอบความถูกต้องอนุญาตให้ใช้ได้_ในขณะที่ข้อความแสดงข้อผิดพลาดระบุว่าไม่มีอักขระพิเศษ การตรวจสอบในการเขียนซ้ำด้านบนทำได้โดยใช้ (อัปเดตตามความคิดเห็นด้านล่าง):
value.isalpha() and value.isascii()
ซึ่งจะดำเนินการได้เร็วกว่า regex (ยกเว้นกรณีที่ผู้ใช้จะช่วยให้การป้อนค่าที่ไม่ถูกต้อง\$ 10^ n \$บางครั้งซึ่งรูปแบบที่คอมไพล์ไว้แล้วอาจทำงานได้ดีกว่าเล็กน้อย)
ประสิทธิภาพ
บางสิ่งที่สามารถเปลี่ยนแปลงได้เพื่อให้โค้ดมีประสิทธิภาพมากขึ้น:
แทนการเชื่อมโยง (ท้าย) เพื่อสตริงผลักดันไปยังรายชื่อและที่ปลายใช้
mode_string"".join()รายละเอียดเพิ่มเติมได้ที่กองมากเกินคุณสามารถทำให้โปรแกรมของคุณรองรับระบบ linux (* nix) ได้เช่นกัน
clsพึ่งพาเฉพาะในหน้าต่างเป็นสายระบบของคุณ บางที ( นำมาจาก Stack Overflow ):def clear(): os.system("cls" if os.name == "nt" else "clear")มี 2 ฟังก์ชันที่มีชื่อคล้ายกันมาก:
start_cipher(mode...)และstart_cipher_mode(mode). ซึ่งทำให้มันเป็นจริงยากที่จะรู้ว่าที่หนึ่งอย่างแท้จริงเริ่มต้นเลขศูนย์ บางทีอาจมี 2 ฟังก์ชั่นที่แยกจากกันencryptและdecrypt?เมื่อใช้การทำงานแบบโมดูโลคุณสามารถลบเงื่อนไขต่อไปนี้:
if cipher_alphabet_index == len(cipher_alphabets): cipher_alphabet_index = 0และจะมีลักษณะดังนี้:
result.append(alphabet[cipher_alphabets[cipher_alphabet_index % alphabets_length].find(char)]เนื่องจากคุณใช้เฉพาะ
alphabetสตริงเพื่อทำงานกับค่าดัชนีของอักขระในนั้นให้สร้างพจนานุกรม การค้นหาในพจนานุกรมคือ\$ O(1) \$เทียบกับ\$ O(n) \$สำหรับ.find(). นี่จะเป็น:from itertools import count alphabet_map = dict(zip(alphabet, count()))จาก 2 จุดข้างต้นเป็นที่ชัดเจนว่าคุณไม่จำเป็นต้องใช้อักขระ / ตัวอักษรหลังจากป้อนข้อมูลโดยผู้ใช้ เฉพาะค่าดัชนีโมดูโลเท่านั้นที่มีความสำคัญ สิ่งนี้อาจเป็นเรื่องยากที่จะเข้าใจ / นำไปใช้โดยไม่ต้องมีความเข้าใจทางคณิตศาสตร์มากนักดังนั้นคุณสามารถข้ามสิ่งนี้ไปได้ในตอนนี้
@hjpotter ครอบคลุมความคิดเห็นส่วนใหญ่ของฉัน
บูลีน
Python มีแนวคิดเกี่ยวกับค่าที่แท้จริงและเป็นเท็จดังนั้นจึงเป็นที่นิยมในการปฏิบัติต่อค่าเป็นบูลีนโดยตรงแทนที่จะเปรียบเทียบกับค่าจริงหรือเท็จ
return True if re.match("^[a-zA-Z_]*$", input_string) else False
สามารถทำให้ง่ายขึ้นเพื่อ:
return re.match("^[a-zA-Z_]*$", input_string)
elif string_is_alpha(alphabet) is False:
สิ่งนี้สามารถทำให้ง่ายขึ้นเพื่อ:
elif not string_is_alpha(alphabet):
ถ้าทั่วไปคุณแทบไม่ต้องการใช้ "is" เพื่อเปรียบเทียบ (ข้อยกเว้นหลักกำลังเปรียบเทียบกับNone)
การรวบรวมนิพจน์ทั่วไป
นี่เป็นการปรับปรุงประสิทธิภาพที่ไม่จำเป็น แต่อาจมีประโยชน์ที่จะทราบในภายหลัง:
การเรียกไปที่re.matchจำเป็นต้องรวบรวม regexp ทุกครั้งที่เรียก คุณสามารถmatchคอมไพล์regexp ล่วงหน้าได้หนึ่งครั้งจากนั้นเรียกใช้วัตถุที่คอมไพล์แล้วเพื่อเร่งความเร็ว
ลูกโลก
เกือบทุกครั้งที่ฉันไปถึงglobalคีย์เวิร์ดนั้นมันกลายเป็นข้อผิดพลาด
ฉันไม่คิดว่าคุณจำเป็นต้องประกาศตัวระบุการสาธิตเป็นส่วนกลาง ควรจะพร้อมใช้งานอยู่แล้ว (เพื่ออ่านจากเท่านั้น - หากคุณพยายามเขียนถึงพวกเขาคุณจะกำหนดตัวแปรใหม่ในขอบเขตใหม่โดยซ่อนต้นฉบับ)