What's wrong with my vigenere cypher encrypt f

2019-08-23 07:44发布

I get the error string index out of range inside of the encrypt function I don't know how to get rot to repeat over text. the code only works when both inputs are the same length. i want to keep the alphabet_position and the rotate_character functions the same if i can.

alpha_lower_list = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", 
"l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
alpha_upper_list = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", 
"L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]   

def alphabet_position(letter):     
    if letter in alpha_upper_list:
        return alpha_upper_list.index(letter)
    else:
        return alpha_lower_list.index(letter)

def rotate_character(char, rot):
    rotated_letter = ''
    if char.isalpha():
        rotate = alphabet_position(char) + rot
        if rotate < 26:
            if char in alpha_upper_list:
                rotated_letter = alpha_upper_list[rotate]
                return(rotated_letter)
            else:
                rotated_letter = alpha_lower_list[rotate]
                return(rotated_letter)
        else:
            if char in alpha_upper_list:
                rotated_letter = alpha_upper_list[rotate % 26]
                return(rotated_letter)
            else:
                rotated_letter = alpha_lower_list[rotate % 26]
                return(rotated_letter)
    else:
        return(char)

def encrypt(text, rot):
    lis = []
    for i in range(len(text)):
        lis.append(rotate_character(text[i], alphabet_position(rot[i])))

    return (''.join(lis))

def main():
    user_text = input("Type a message: ")
    rotate_by = input("Rotate by: ")

    print(encrypt(user_text, rotate_by))

if __name__ == '__main__':
    main()

1条回答
叛逆
2楼-- · 2019-08-23 08:11

You are going out-of-bounds by using the iterator in rot[i] in line 36 of your program - i will go up to the length of your plaintext, which can be larger than the key.

Try doing a module-div by length of the key, that way you should wrap around the key nicely:

lis.append(rotate_character(text[i], alphabet_position(rot[i % len(rot)])))

Edit You were still unhappy with the result your script returned, so I dug a bit deeper. The underlying problem is that you are trying to implement what some online-tools for Vigenere call "enhanced mode": neither plain- nor ciphertext are guarenteed to be from [a-zA-Z], but may "special characters", e.g. elements from [0-9] or characters like <space>, <, etc.

In case your script encounters a special character, it will not rotate it, but rather copy it over as-is, as done in the else-branch of rotate_character; this is correct. In encrypt(), however, you consume what we shall call a "keysymbol" for every symbol of the plaintext you encounter. This means that effectively you are "wasting" a keysymbol on a plaintext symbol that will not be encrypted (vulgo rotated) at all.

Once you realize this, the fix becomes evident: when we encounter a special character, copy it over into the output list, but don't advance the keystream. The keystream should only be advanced if we actually need to consume a keysymbol.

Translated into code:

def encrypt(text, rot):
    lis = []
    keystream = 0
    for i in range(len(text)):
        keychar = keystream % len(rot)
        if text[i].isalpha():
            lis.append(rotate_character(text[i], alphabet_position(rot[keychar])))
            keystream += 1
        else:
            lis.append(text[i])

    return (''.join(lis))
查看更多
登录 后发表回答