Борьба с ASCII Loop для Цезаря Шифра - PullRequest
2 голосов
/ 30 октября 2019

Это мой первый вопрос, опубликованный в stackoverflow, любая помощь / критика / помощь приветствуются ... Мне нужна вся помощь, которую я могу получить, ха-ха.

Я очень новичок в программировании.

Цель состоит в том, чтобы создать шифр Цезаря, который шифрует и дешифрует ввод строки пользователя в ord, добавляет ввод пользователя offset_value, а затем заменяет его обратно на символ.

Я использую ASCIIперсонажи. Проблема в том, что мне нужно изолировать шифрование и дешифрование в ASCII 32 ('a') - ASCII 126 ('~'). Я не уверен, как создать функцию, которая проходит через 94 символа.

Так, например, если char равен 'Z', то есть ASCII или 94, если мы добавим пользовательский ввод offset_value, который мог бы быть 90, что сделало бы орд 184. Что находится за пределами диапазона.

Это приводит к реальной проблеме, грубому шифрованию. Это работает ... вроде. Необходимо отобразить все возможные результаты, когда offset_value изменяется между 1 и 94. Например, что произойдет, если мы расшифруем каждую букву с offset_value из x (x - любое число от 1 до 94).

Вместо этого он просто продолжает расти и расти.

Имеет ли что-нибудь из этого смысл?

Мой код приведен ниже. Я знаю, что еще не создал ни одной функции, но сделаю это.

Заранее спасибо, ребята.

choice = 0
list1 = [1, 2, 3, 4]
list2 = list(range(1, 95))
new_ord = 0
index = 1
encryption = ''
decryption = ''
offset_value = 1


#while loop allows for multiple use, option 4 ends loop
while choice != 4:
    print('*** Menu ***')
    print('\r')
    print('1. Encrypt string')
    print('2. Decrypt string')
    print('3. Brute force decryption')
    print('4. Quit')
    print('\r')
    choice = int(input('What would you like to do [1,2,3,4]? '))

    #invalid user input loop, valid entry ends loop
    while choice not in list1:
        print('\r')
        print('Invalid choice, please enter either 1, 2, 3 or 4.')
        print('\r')
        choice = int(input('What would you like to do [1,2,3,4]? '))

    #user chooses 'encrypt string', stores data
    if choice == 1:
        print('\r')
        string_to_encrypt = str(input('Please enter string to encrypt: '))
        offset_value = int(input('Please enter offset value (1 to 94): '))

        #invalid user input loop, valid entry ends loop
        while offset_value not in list2:
            offset_value = int(input('Please enter offset value (1 to 94): '))

        #encryption loop for length of string_to_encrypt
        for letter in string_to_encrypt:
            encryption = encryption + chr((ord(letter) + offset_value))

        #prints encrypted string
        print('\r')
        print('Encrypted string:')
        print(encryption)
        print('\r')

        #clears ecryption data
        encryption = ''

    #user chooses 'decrypt string', stores data
    elif choice == 2:
        print('\r')
        string_to_decrypt = str(input('Please enter string to decrypt: '))
        offset_value = int(input('Please enter offset value (1 to 94): '))

        #invalid user input loop, valid entry ends loop
        while offset_value not in list2:
            offset_value = int(input('Please enter offset value (1 to 94): '))

        #decryption loop for length of string_to_decrypt
        for letter in string_to_decrypt:
                decryption = decryption + chr((ord(letter) - offset_value))

        #prints decrypted string
        print('\r')
        print('Decrypted string:')
        print(decryption)
        print('\r')

        #clears decryption data
        decryption = ''

    #user chooses 'Brute Force Decryption
    elif choice == 3:
        string_to_decrypt = str(input('Please enter string to decrypt: '))
        for number in range(94):
            for letter in string_to_decrypt:
                decryption = decryption + chr((ord(letter) - offset_value))
            print('Offset: ', index, '= Decrypted String: ', decryption)
            offset_value = offset_value + 1
            index = index + 1
            decryption = ''

    #user chooses 'quit'
        print('Goodbye.')

Ответы [ 4 ]

1 голос
/ 30 октября 2019

Вместо того, чтобы переписать вашу программу для вас, я опишу, как решить такую ​​проблему, которая в основном включает в себя небольшую математику.

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

Это можно сделать математически для каждого значения в диапазоне, вычитая минимальное значение в диапазонедобавляя к этому смещение, вычисляя сумму по модулю разности между верхним и нижним значениями диапазона (+1), а затем, наконец, добавляя минимальное значение обратно, чтобы компенсировать его первоначальное вычитание. Важной частью является операция по модулю % для ограничения суммы.

Код в функции offset_range() ниже показывает, как сделать все это внутри списка понимания , который является сокращенным способом создания списков в Python.

Это и функция print_range() также служат для иллюстрации того, как функции определяются в Python.

def print_range(name, range_):
    """ Pretty-print given range of character ordinals. """
    ints = ', '.join('{:3d}'.format(val) for val in range_)
    chrs = ', '.join('{:>}'.format(repr(chr(val))) for val in range_)
    print('{}: [{}]'.format(name, ints))
    print('{}  [{}]'.format(len(name)*' ', chrs))

def offset_range(offset_value, range_):
    """ Add offset value to integers in given range and wrap result. """
    min_val, max_val = range_[0], range_[-1]  # Get upper and lower limits.
    diff_plus_1 = (max_val - min_val) + 1  # Their difference + 1 for computation.
    # Use a list comprehension to create new list.
    offset_range = [(((val - min_val) + offset_value) % diff_plus_1) + min_val
                        for val in range_]
    return offset_range

ascii_range = list(range(ord('a'), ord('~')+1))
print_range('ascii_range', ascii_range)

print()
offset_range = offset_range(1, ascii_range)
print_range('offset_range', offset_range)

Вывод:

ascii_range: [ 97,  98,  99, 100, 101, 102, 103, 104, 105, ... 122, 123, 124, 125, 126]
             ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', ... 'z', '{', '|', '}', '~']

offset_range: [ 98,  99, 100, 101, 102, 103, 104, 105, ... 122, 123, 124, 125, 126,  97]
              ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', ... 'z', '{', '|', '}', '~', 'a']
0 голосов
/ 30 октября 2019

Некоторые решения, использующие ordinal % len(alphabet), уже предлагались. Я хочу представить немного другое решение. Все решение вращается вокруг str.maketrans, что может обеспечить простой способ реализации шифров.

Если вы передадите две строки одинаковой длины в str.maketrans, он возвращает словарь, который отображает порядковые значениясимволы в первом, к порядковым значениям символов во втором в том порядке, в котором они появляются.

Например:

>>> str.maketrans("ABC", "DEF")
{65: 68, 66: 69, 67: 70}
>>> 

Прописные буквы 'A' (65) ->Прописная буква D (68). И т. Д. Вы уже можете видеть, как это можно использовать для реализации вашего Цезаря-шифра - при условии, что вы можете генерировать две строки одинаковой длины, где вторая строка смещается на некоторое количество от первой (а затем оборачивается вв начале), вы можете легко сгенерировать одну из этих таблиц перевода (в документации это называется в нескольких местах, но это просто старый словарь Python) и сделать так, чтобы она выполняла большую часть тяжелой работы.

Мое решение:

get_alphabet, учитывая минимальный и максимальный порядковый номер, возвращает строку, содержащую все символы в требуемом алфавите.

get_offset_alphabet, учитывая алфавит (строку) и смещение (целое число), возвращает переданный в алфавите, но смещение и завернутый. Это достигается созданием бесконечного символьного итератора с использованием itertools.cycle, обрезкой и отбрасыванием некоторого количества с начала итератора в зависимости от смещения, а затем построением строки равной длины из следующих n символов из итератора (где 'n' - длина исходного алфавита). Это также поддерживает отрицательные смещения.

В main мы получаем два наших алфавита - первый не изменяется, а второй смещается и переносится. Мы создаем таблицу перевода (словарь), утверждаем, что все символы в нашем открытом тексте могут быть сопоставлены с каким-либо другим символом в соответствии с нашей таблицей перевода, а затем выполняем фактический перевод для получения зашифрованного текста. str.maketrans также имеет третий необязательный параметр, который можно использовать для сопоставления символов, которые не имеют сопоставления, с None, который я здесь не использовал.

def get_alphabet(min_ordinal, max_ordinal):
    assert isinstance(min_ordinal, int)
    assert isinstance(max_ordinal, int)
    assert 0 <= min_ordinal <= 255
    assert 0 <= max_ordinal <= 255
    assert min_ordinal <= max_ordinal
    return "".join(map(chr, range(min_ordinal, max_ordinal+1)))

def get_offset_alphabet(alphabet, *, offset):
    assert isinstance(alphabet, str)
    assert isinstance(offset, int)
    assert alphabet

    from itertools import cycle, islice
    char_iterator = cycle(alphabet)
    _ = list(islice(char_iterator, [offset, len(alphabet)+offset][offset<0]))
    return "".join(islice(char_iterator, len(alphabet)))

def main():

    from_alphabet = get_alphabet(32, 126)
    to_alphabet = get_offset_alphabet(from_alphabet, offset=1)

    translation = str.maketrans(from_alphabet, to_alphabet)

    plaintext = "Hello World"
    assert all(ord(char) in translation for char in plaintext)

    ciphertext = plaintext.translate(translation)
    print(ciphertext)

    return 0


if __name__ == "__main__":
    import sys
    sys.exit(main())
0 голосов
/ 30 октября 2019

Если ваша цель - понять, как вращать буквенные символы, вам может пригодиться следующая программа:

#! /usr/bin/env python3
"""Demonstrate One Approach to Caesar Cipher.

This program is designed to show how encryption and decryption can be easily
implemented using the incredibly insecure Caesar Cipher. As a teaching tool,
this form of encryption can be used to teach programming algorithms but may
not be used for securing data. See lines 37 - 41 for meat of mathematics."""

CAESAR_OFFSET = 1


def main():
    """Accept string to handle and show result of encryption and decryption."""
    text = input('>>> ')
    code = encrypt(text)
    print(' ' * 4 + code)
    text = decrypt(code)
    print(' ' * 4 + text)


def encrypt(plain):
    """Rotate a string in positive direction of caesar offset."""
    return rotate(plain, +CAESAR_OFFSET)


def decrypt(secret):
    """Rotate a string in negative direction of caesar offset."""
    return rotate(secret, -CAESAR_OFFSET)


def rotate(text, offset):
    """Rotate alpha characters in the caesar cipher by offset characters."""
    mutable_string = list(text)
    for index, character in enumerate(mutable_string):
        for a, z in 'AZ', 'az':
            if a <= character <= z:
                ordinal = ord(character)    # change character into a number
                ordinal -= ord(a)           # put number into range of 0 - 25
                ordinal += offset           # add the factor by which we rotate
                ordinal %= 26               # put number back in range (0 - 25)
                ordinal += ord(a)           # convert back to character code
                mutable_string[index] = chr(ordinal)
    return ''.join(mutable_string)


if __name__ == '__main__':
    main()
0 голосов
/ 30 октября 2019

Шифр ​​Цезаря использует алфавит с индексом, начинающимся с 0. Таким образом, вам нужна некоторая функция для преобразования одного из диапазона от 32 до 126, от 0 до 126 - 32 = 98. Затем вам нужно выполнить вычисления mod 99, поскольку существует 99 элементов с индексом от 0 до 98.

В общем, идея программирования - победить, разделив проблему на несколько функций. Большинство ответов уже будут предполагать, что вы бегло и делаете это одним лайнером. Тем не менее, это, как правило, только сбивает вас с толку и позволяет узнать, как не программировать.

Итак, ниже я выписал шифрование . Теперь, если вы создадите процедуру decryption , вы можете повторно использовать метод decrypt для своей попытки перебора. Вы увидите, что теперь намного проще делать все правильно.

LO_CHAR = ord('A')
HI_CHAR = ord('~')
CHARS = HI_CHAR - LO_CHAR + 1

def char_to_index(char):
    o = ord(char)
    if (o < LO_CHAR or o > HI_CHAR):
       raise
    return o - LO_CHAR

def index_to_char(index):
    if (index < 0 or index >= CHARS):
       raise
    return chr(index + LO_CHAR)

def encrypt_index(key, index):
    return (index + key) % CHARS

def encrypt(key, plaintext):
    ciphertext = ''
    for plain_char in plaintext:
        plain_index = char_to_index(plain_char)
        cipher_index = encrypt_index(key, plain_index)
        cipher_char = index_to_char(cipher_index)
        ciphertext = ciphertext + cipher_char
    return ciphertext

... счастливое программирование ...

...