问题
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:
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))
来源:https://stackoverflow.com/questions/46865005/whats-wrong-with-my-vigenere-cypher-encrypt-function