与凯撒密码的 ASCII 循环作斗争

Struggling with ASCII Loop for Caesar Cipher

这是我发布到 Whosebug 的第一个问题,感谢所有 help/critique/assistance...我需要我能得到的所有帮助哈哈。

我对编程很陌生。

目的是创建一个凯撒密码,加密和解密用户字符串输入到一个ord,添加用户输入offset_value,然后将其改回字符。

我正在使用 ASCII 字符。问题是我需要将加密和解密隔离为 ASCII 32 ('a') - ASCII 126 ('~')。我不确定如何创建一个循环返回 94 个字符的函数。

因此,例如,如果字符是 'Z',即 ASCII ord 94,如果我们添加用户输入 offset_value,这可能是 90,这将使 ord 成为 184。这是超出范围。

这就引出了真正的问题,暴力加密。它有效……有点。它需要显示 offset_value 在 1 和 94 之间变化的所有可能结果。例如,如果我们用 x 的 offset_value(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.')

我不会为您重写程序,而是要描述如何解决这样的问题,这主要涉及做一些数学运算。

你的问题的本质归结为如何将给定范围的整数值偏移给定的量并将结果包装起来,使它们仍然在原始值范围内。

这可以通过减去范围内的最小值,将偏移量添加到范围内,计算范围内高值和低值之间的差值的总和(+1)来计算范围内的每个值,然后最后将最小值加回去以补偿最初减去它的结果。关键的部分是modulo%操作来约束求和。

下面 offset_range() 函数中的代码显示了如何在 list comprehension 中执行所有操作,这是在 Python 中创建列表的 shorthand 方法。

那个和 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 开始的字母表。因此您需要一些函数将范围从 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

...快乐编程...

已经提供了一些使用 ordinal % len(alphabet) 的解决方案。我想提出一个稍微不同的解决方案。整个解决方案围绕str.maketrans,它可以提供一种实现密码的简单方法。

如果将两个等长的字符串传入str.maketrans,它returns一个字典,它将第一个字符的序数值映射到第二个字符的序数值按出现顺序排在第二位。

例如:

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

大写 'A' (65) -> 大写 'D' (68)。等等。您已经可以看到如何使用它来实现您的凯撒密码 - 只要您可以生成两个等长的字符串,其中第二个字符串从第一个字符串偏移一定量(然后环绕到开始),你可以很容易地生成这些翻译 tables 之一(文档在几个地方称它为它,但它只是一个普通的旧 Python 字典)并让它完成大部分繁重的工作.

我的解决方案:

get_alphabet,给定最小和最大序数,returns 一个包含所需字母表中所有字符的字符串。

get_offset_alphabet,给定一个字母表(字符串)和一个偏移量(整数),returns 传入的字母表但偏移量和换行。这是通过使用 itertools.cycle 创建一个无限字符迭代器,根据偏移量从迭代器的开头切掉并丢弃一些量,然后从下一个 n 个字符构造一个等长字符串来实现的迭代器(其中 'n' 是原始字母表的长度)。这也支持负偏移量。

main 中,我们得到了两个字母 - 第一个未更改,第二个偏移并换行。我们创建一个翻译 table(字典),断言我们明文中的所有字符都可以根据我们的翻译 table 映射到其他某个字符,然后执行实际翻译以产生密文。 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())