Mật mã Python 3 Vigenere

Nov 02 2020

Tôi vẫn còn khá mới đối với Python và tôi đang cố gắng xem liệu tôi đã sử dụng hiệu quả các mô-đun, hàm, v.v. hay có cách khác / dễ dàng hơn để làm điều gì đó hay không.

Mật mã Python 3 Vigenere này là bản xây dựng lại mật mã dựa trên JavaScript và dựa trên Windows. Nó chấp nhận bất kỳ ký tự bảng chữ cái nào và có các tùy chọn demo được tích hợp sẵn. Bản demo mật mã sử dụng giai đoạn 1 của mật mã 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()

Trả lời

7 hjpotter92 Nov 02 2020 at 17:35

shebang

Shebang nên chung chung. Bạn hiện đang gọi python, điều này có thể trỏ đến python 2 trên một số hệ thống nhất định.

Một shebang python chung, thân thiện với môi trường ảo là:

#!/usr/bin/env python3

PEP-8

Vài điểm từ hướng dẫn PEP-8 :

  • Bao quanh định nghĩa lớp và chức năng cấp cao nhất bằng hai dòng trống.
  • Sử dụng các dòng trống trong các hàm, một cách tiết kiệm, để chỉ ra các phần hợp lý.
  • Hằng số thường được định nghĩa ở cấp độ mô-đun và được viết bằng tất cả các chữ cái in hoa với dấu gạch dưới ngăn cách các từ.

PEP-484

Nhập các chức năng gợi ý giúp bạn dễ dàng theo dõi các chức năng của mình hơn. Kiểm tra PEP-484 .

if __name__ khối

Đặt logic thực thi của tập lệnh của bạn bên trong if __name__ == "__main__"khối. Có thể kiểm tra phần giải thích mô tả hơn trên Stack Overflow .

Logic dư thừa

Trong mã của bạn, bạn có 5 chức năng khác nhau, chỉ để đọc đầu vào của người dùng. Tất cả chúng đều có chung một công việc là:

  1. vòng lặp vô hạn
  2. yêu cầu người dùng nhập
  3. chuyển sang chữ hoa
  4. xác thực nếu đầu vào trống (hoặc trong một tập hợp các giá trị hợp lệ)
  5. trong trường hợp giá trị trống, hãy trả về giá trị mặc định
  6. trở lại với giá trị

Tất cả điều này có thể được xử lý bởi một chức năng duy nhất:

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 / xác thực

Regex để xác thực đầu vào cho phép _, trong khi thông báo lỗi cho biết không có ký tự đặc biệt. Kiểm tra trong phần viết lại ở trên được thực hiện bằng cách sử dụng (cập nhật dựa trên nhận xét bên dưới):

value.isalpha() and value.isascii()

sẽ hoạt động nhanh hơn regex (trừ khi người dùng tiếp tục nhập các giá trị sai \$ 10^ n \$thời gian mà mẫu biên dịch trước có thể hoạt động tốt hơn một chút).

Hiệu suất

Một số điều có thể được thay đổi để làm cho mã hoạt động hiệu quả hơn:

  1. Thay vì nối (nối) vào chuỗi mode_string, hãy đẩy đến một danh sách và ở cuối, hãy sử dụng "".join(). Thêm chi tiết về Stack Overflow .

  2. Bạn cũng có thể làm cho chương trình của mình hỗ trợ hệ thống linux (* nix). Sự phụ thuộc duy nhất vào windows là lệnh gọi hệ thống của bạn tới cls. Có lẽ ( lấy từ Stack Overflow ):

    def clear():
        os.system("cls" if os.name == "nt" else "clear")
    
  3. Có 2 hàm có tên rất giống nhau: start_cipher(mode...)start_cipher_mode(mode). Điều này làm cho nó thực sự khó khăn để biết cái nào đang thực sự bắt đầu mật mã . Có lẽ, có 2 chức năng riêng biệt encryptdecrypt?

  4. Sử dụng thao tác modulo, bạn có thể loại bỏ điều kiện sau:

    if cipher_alphabet_index == len(cipher_alphabets):
        cipher_alphabet_index = 0
    

    và trông giống như:

    result.append(alphabet[cipher_alphabets[cipher_alphabet_index % alphabets_length].find(char)]
    
  5. Vì bạn chỉ sử dụng alphabetchuỗi để thực sự làm việc với các giá trị chỉ mục của các ký tự trong đó, hãy tạo một từ điển. Tra cứu trong từ điển là \$ O(1) \$so với \$ O(n) \$cho .find(). Điều này sẽ:

    from itertools import count
    alphabet_map = dict(zip(alphabet, count()))
    
  6. Từ 2 điểm trên, rõ ràng là bạn không thực sự cần các ký tự / bảng chữ cái sau khi người dùng nhập. Chỉ mô-đun giá trị chỉ mục mới quan trọng. Điều này có thể khó hiểu / khó thực hiện nếu không có hiểu biết toán học đáng kể, vì vậy bạn có thể bỏ qua điều này ngay bây giờ.

1 Oddthinking Nov 03 2020 at 00:52

@hjpotter đã bao gồm hầu hết các nhận xét của tôi.

Booleans

Python có khái niệm về giá trị true và falsy , vì vậy chỉ nên coi các giá trị là boolean trực tiếp hơn là so sánh chúng với True hoặc False.


return True if re.match("^[a-zA-Z_]*$", input_string) else False

có thể được đơn giản hóa thành:

return re.match("^[a-zA-Z_]*$", input_string)

    elif string_is_alpha(alphabet) is False:

Điều này có thể được đơn giản hóa thành:

    elif not string_is_alpha(alphabet):

Nếu chung chung, bạn hiếm khi muốn sử dụng "is" để so sánh. (Ngoại lệ chính là so sánh với None.)

Biên dịch các biểu thức chính quy

Đây gần như chắc chắn là một cải tiến hiệu suất không cần thiết, nhưng có thể hữu ích để biết về sau:

Lệnh gọi re.matchcần biên dịch regexp mỗi khi nó được gọi. Bạn có thể biên dịch trước regexp một lần, sau đó gọi matchđối tượng đã biên dịch để tăng tốc nó.

Globals

Hầu như mỗi khi tôi tiếp cận với globaltừ khóa, nó đều bị nhầm lẫn.

Tôi không nghĩ rằng bạn cần phải khai báo số nhận dạng demo là toàn cục; chúng nên đã có sẵn để sử dụng (chỉ để đọc - nếu bạn cố gắng ghi vào chúng, bạn sẽ xác định một biến mới trong phạm vi mới, ẩn các biến gốc).