Реализация Baconian Cipher Python

Я пытаюсь реализовать различные подстановочные шифры в python. Я реализовал шифр Baconian с использованием словаря, в котором буквы простого текста являются ключами, а буквы зашифрованного текста - соответствующими значениями. Есть ли способ сделать следующий код короче или эффективнее, или есть лучший подход к реализации шифрования.

Программа Python для реализации шифрования Baconian

'''This script uses a dictionary instead of 'chr()' & 'ord()' function'''

'''
Dictionary to map plaintext with ciphertext
(key:value) => (plaintext:ciphertext)
This script uses the 26 letter baconian cipher
in which I,J & U,V have distinct patterns
'''
lookup = {'A':'aaaaa', 'B':'aaaab', 'C':'aaaba', 'D':'aaabb','E':'aabaa',
    'F':'aabab', 'G':'aabba', 'H':'aabbb', 'I':'abaaa', 'J':'abaab',
    'K':'abaab', 'L':'ababa', 'M':'ababb', 'N':'abbaa', 'O':'abbab',
    'P':'abbba', 'Q':'abbbb', 'R':'baaaa', 'S':'baaab', 'T':'baaba',
    'U':'babaa', 'V':'babab', 'W':'babaa', 'X':'babab', 'Y':'babba' 
    'Z':'babbb'}
#Function to encrypt the string according to the cipher provided
def encrypt(message):
    cipher = ''
    for letter in message:
        #checks for space
        if(letter != ' '):
            #adds the ciphertext corresponding to the plaintext from the dictionary
            cipher += lookup[letter]
        else:
            #adds space
            cipher += ' '

    return cipher

#Function to decrypt the string according to the cipher provided
def decrypt(message):
    decipher = ''
    i = 0

    #emulating a do-while loop
    while True :
        #condition to run decryption till the last set of ciphertext
        if(i < len(message)-4):
            #extracting a set of ciphertext from the message
            substr = message[i:i+5]
            #checking for space as the first character of the substring
            if(substr[0] != ' '):
                '''
                This statement gets us the key(plaintext) using the values(ciphertext)
                Just the reverse of what we were doing in encrypt function
                '''
                decipher += list(lookup.keys())[list(lookup.values()).index(substr)]
                i += 5 #to get the next set of ciphertext
            else:
                #adds space
                decipher += ' '
                i += 1 #index next to the space
        else:
            break #emulating a do-while loop

    return decipher

def main():
    message = "ALICE KILLED BOB"
    result = encrypt(message.upper())
    print (result)

    message = "aaaaaababaabaaaaaabaaabaa abaababaaaababaababaaabaaaaabb aaaababbabaaaab"
    result = decrypt(message.lower())
    print (result)

#Executes the main function
if __name__ == '__main__':
    main()

Я использую большинство подстановочных шифров, используя словари, и я хочу знать, есть ли другой подход к этому, поскольку мне скучно использовать словари.

11 голосов | спросил palash25 26 J0000006Europe/Moscow 2017, 10:27:40

2 ответа


14

Ошибка

Буквы J & K, U & W и V & X имеют один и тот же шаблон. Это не то, что говорит спецификация : либо I & J и U & V использует один и тот же шаблон, или ни одна из букв не делает.

Стиль

Строковые литералы - это не комментарии. Если вы хотите две строки комментариев, вы начинаете свои две строки с помощью #. Также используйте привычку помещать пробел после знака #, это облегчает читаемость.

Вам также не нужно помещать круглые скобки в простые условия.

Наконец, PEP 8 рекомендует использовать ALL_CAPS для обозначения констант, например LOOKUP

Генераторы

Конкатенация строк с помощью += выполняется медленно, поскольку строки являются неизменяемыми, а Python необходимо скопировать всю строку каждый раз, когда вы добавляете бит в конец. Для улучшения производительности рекомендуется использовать ''.join. Для этого проще разделить каждую функцию на две части: одну, которая выполняет итерацию по входу и соединяет преобразованный вывод, другой, который генерирует преобразованный вывод. Что-то вроде:

def encrypt_letter(letter):
    """Convert a plain-text ascii letter into its Bacon's form.

    Let symbols and non ascii letter fail through gracefully.
    """
    return LOOKUP.get(letter.upper(), letter.lower())


def encrypt(message):
    """Encrypt a message using Bacon's cipher"""
    return ''.join(map(encrypt_letter, message))

Еще одна вещь, которую следует отметить в этой реализации: вы не должны требовать, чтобы вход был правильно отформатирован, вы должны применять его в своих функциях (использование upper и lower в пределах вашей функции, а не до ее вызова)

Do-то время

В Python цикл do-while эмулируется, используя что-то по строкам:

while True:
    do_something()
    if ...:
        break

В вашем случае наличие if непосредственно в качестве первого действия while и else: break, вам просто нужен простой while:

while i < len(message) - 4:
    substr = message[i:i + 5]
    ...
ответил Mathias Ettinger 26 J0000006Europe/Moscow 2017, 11:02:07
10

В Python, когда вы добавляете более двух строк, вы, вероятно, ошибаетесь. Это связано с тем, что при добавлении двух строк необходимо создать новую строку, скопировать содержимое первой строки и затем скопировать вторую строку. Это относительно дорого, поэтому его следует избегать. Вы можете просто скопировать строки в списке, а затем str.join их в конце.

В Python вам не нужно () вокруг выражения в if, for или while.

У Python есть способ добавить документацию к функции (или классу), они называются docstring s. Как они выглядят, это указано в PEP257 , но это самый простая форма:

def f(a, b):
    """Returns the sum of `a` and `b`."""
    return a + b

В ваших комментариях над функциями (которые я перейду в docstrings ниже), вы заявляете, что функции en /decrypt сообщение, используя данный шифр. Но обе функции фактически не принимают никакого шифрования. Поэтому вам нужно либо изменить подпись функции, либо docstring.

Вам также не хватало , в конце вашей второй до последней строки в lookup

Для функции decrypt я бы сначала обратил код cipher, а затем используйте это для расшифровки сообщения.

Я бы добавил в словарь ' ': ' ', поэтому нам не нужно делать какой-либо специальный случай для пробела при шифровании.

С этими изменениями ваш код будет выглядеть следующим образом:

lookup = {'A': 'aaaaa', 'B': 'aaaab', 'C': 'aaaba', 'D': 'aaabb', 'E': 'aabaa',
          'F': 'aabab', 'G': 'aabba', 'H': 'aabbb', 'I': 'abaaa', 'J': 'abaab',
          'K': 'abaab', 'L': 'ababa', 'M': 'ababb', 'N': 'abbaa', 'O': 'abbab',
          'P': 'abbba', 'Q': 'abbbb', 'R': 'baaaa', 'S': 'baaab', 'T': 'baaba',
          'U': 'babaa', 'V': 'babab', 'W': 'babaa', 'X': 'babab', 'Y': 'babba',
          'Z': 'babbb', ' ': ' '}


def encrypt(message, cipher):
    """Ecrypt the string according to the cipher provided."""
    # return ''.join(map(lookup.get, message))
    encrypted = []
    for letter in message:
        encrypted.append(lookup[letter])
    return ''.join(encrypted)


def decrypt(message, cipher):
    """Decrypt the string according to the cipher provided."""
    plaintext = []
    i = 0
    reverse_cipher = {v: k for k, v in cipher.items()}
    # emulating a do-while loop
    while True:
        # condition to run decryption till the last set of ciphertext
        if i < len(message) - 4:
            # extracting a set of ciphertext from the message
            substr = message[i:i + 5]
            # checking for space as the first character of the substring
            if substr[0] != ' ':
                # This statement gets us the key(plaintext) using the values(ciphertext)
                # Just the reverse of what we were doing in encrypt function
                plaintext.append(reverse_cipher[substr])
                i += 5  # to get the next set of ciphertext
            else:
                # adds space
                plaintext.append(' ')
                i += 1  # index next to the space
        else:
            break  # emulating a do-while loop

    return ''.join(plaintext)


def main():
    message = "ALICE KILLED BOB"
    result = encrypt(message.upper(), lookup)
    print(result)

    message = "aaaaaababaabaaaaaabaaabaa abaababaaaababaababaaabaaaaabb aaaababbabaaaab"
    result = decrypt(message.lower(), lookup)
    print(result)

# Executes the main function
if __name__ == '__main__':
    main()

Ваша функция шифрования может быть значительно упрощена при использовании map. map проходит через итерабельность и применяет функцию ко всем значениям. В качестве функции для применения мы можем использовать функцию dict.get lookup.

def encrypt(message, cipher):
    """Ecrypt the string according to the cipher provided."""
    return ''.join(map(lookup.get, message))

Вы также можете сделать функцию decrypt короче, используя списки, но в этом случае это не так читаемо:

def decrypt(message, cipher):
    reverse_cipher = {v: k for k, v in cipher.items()}
    return ' '.join(''.join(reverse_cipher[word[i:i + 5]]
                            for i in range(0, len(word), 5))
                    for word in message.split(' '))
ответил Graipher 26 J0000006Europe/Moscow 2017, 10:59:16

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132