Vigenere密码,如何处理超出字符值范围的序数值
Posted
技术标签:
【中文标题】Vigenere密码,如何处理超出字符值范围的序数值【英文标题】:Vigenere cipher, how to deal with ordinal values out of range of character values 【发布时间】:2015-12-10 10:15:41 【问题描述】:我是一名学生,正在做我的课程,我们必须制作一个 Vigenere 密码程序。 我知道我的程序很复杂,但我不知道有任何其他方法可以解决它,而且修复它为时已晚。 当我添加消息的序数值和关键字时,我似乎有一个问题,新的序数值超出了正常字母值的范围。所以它会打印出像这样的奇怪字母 ÐÑÐÑÐÑÐÑÐÑÐÑÐÑÐÑ。
这是我的代码:
newmessage1 = []
stringposition = 0
number1 = 0
mesletter = 0
keyletter = 0
output =[]
keylist = []
stringofnumbs = []
question = input('Would you like to encrypt, decrypt a message or quit?')
encdec=[]
if question == 'encrypt'or'encrypt a string'or'ENCRYPT'or'encrypt_a_message'or'Encrypt':
message1 = input('Please input a message to encrypt')
keyword1 = input('Please input a keyword ')
if message1.isalpha and keyword1.isalpha:#this check if the message and the keyword only contains letters
messagelength=len(message1)#this sets the variable 'messagelength' to the length of message1
newkeyword=''#this is an empty variable used to insert the length of the new keyword
while len(keyword1)<=len(message1):#this will loop checks that the length of keyword1 is smaller thasn the length of message1.
keyword1+=keyword1#this will repeat the keyword to fit the length of the message
newkeyword=keyword1[:len(message1)]
#this set the 'newkeyword' variable to the length of the new keyword
#(the new keyword is the keyword repeated to fit the length of the message.)
for mesletter in message1:
mesnumber = ord(mesletter)#what it does is it takes every letter in message1 and puts it into its unicode form.
stringofnumbs.append(mesnumber)#once one letter has been put in its unicode value it will append that unicdoe from of that
#letter in the variable 'stringofnumbs',it will do this process for every letter in the message.
for keyletter in keyword1:
keynumber = ord(keyletter)#what it does is it takes every letter in keyword1 and puts it into its unicode form.
keylist.append(keynumber)#once one letter has been put in its unicode value it will
#append that unicdoe from of that letter in the variable 'stringofnumbs',it will do this process for every letter in the message.
temp1 = int(stringofnumbs[stringposition])#temp1 is a variable that holds the ordinal values of letters in message1
temp2 = int(keylist[stringposition-1])#and temp2 is a variable that holds the ordinal values of letters in the keyword1
temp3 = temp2+temp1#temp3 then adds these ordinal values togther
encdec.append(temp3)#the ordinal values added togther are appended to the encdec variable
for number1 in encdec:
newletter1=chr(number1)#this for loop takes every letter in encdec and puts the ordinal values back into charcters
newmessage1.append(newletter1)#then every letter that has been changed to a charcater value is appended to the varibale newmessage1
print (' ')#this leaves a bit of space to make the encoded text clear
print ('the endcoded text is:')#this is just a bit of text to make the encoded text clear to the user
print (''.join(newmessage1))#here the encoded message is printed
else:
('The message or keyword is invalid please use only numbers')#this will print only if the keyword or message doesnt only contain letters and spaces
【问题讨论】:
【参考方案1】:我认为最常见的解决您问题的方法是再次“包装”数字。也就是说,如果你除以你想要允许的最大有效序数,然后使用余数。
例如,使用chr
作为ord
的倒数:
In [0]: chr(122) # I guess this is the last valid character you want?
Out[0]: 'z'
In [1]: chr(123) # Because now we go into symbols
Out[1]: ''
In [2]: 234%122 # We can take the remainder using modulo division
Out[2]: 112
In [3]: chr(234) # Not what you want?
Out[3]: 'ê'
In [4]: chr(234%122) # What you want?
Out[4]: 'p'
In [5]: chr(0) # On further inspection, you also need to add a constant when you divide
Out[5]: '\x00'
In [6]: chr(49) # Try adding 49 after modulo dividing, and that should keep you alphanumeric.
Out[6]: '1'
我实际上不知道这是否会影响你之后的解密,我没有调查过,但你可能想考虑一下。
【讨论】:
【参考方案2】:您使用序数不必要地使事情复杂化。实际上,您使用的是包含 256 个符号的“字母”,而您只对 26 个符号感兴趣。
理想情况下,您只希望符号的数字范围在 0 到 25 之间,一旦达到 26,您就会再次扭曲到 0。您可以使用序数来实现这一点,但您必须使用正确的偏移量。
现在,假设您只使用大写字母。它们的序数从 65 到 90 不等。因此,一旦你达到 91,你就想扭曲到 65。
if temp3 > 90:
temp3 -= 26
另一个问题是您只需添加temp1
和temp2
。 temp1
可以保留为 65 到 90 之间的数字,但 temp2
应该是键偏移量,范围从 0 到 25。因此,当你计算 temp2
时,你真的应该这样做
temp2 = int(keylist[stringposition-1]) - 65
这在概念上可以解决您的问题,但您的算法仍然不正确。 while 循环的目的是将关键字的长度扩展为至少与消息的长度一样长。其他所有内容都必须向左缩进 4 个空格,这样它们就不是循环的一部分。明确地,
while len(keyword1)<=len(message1):
keyword1+=keyword1
newkeyword=keyword1[:len(message1)]
for mesletter in message1:
keynumber = ord(keyletter)
stringofnumbs.append(mesnumber)
for keyletter in newkeyword:
keynumber = ord(keyletter)
keylist.append(keynumber)
temp1 = int(stringofnumbs[stringposition])
temp2 = int(keylist[stringposition]) - 65
temp3 = temp2+temp1
if temp3 > 90:
temp3 -= 26
encdec.append(temp3)
stringposition += 1
etc
在上面的代码块中,我想指出一些我更正的错误。您应该遍历 newkeyword
,而不是 keyword1
,并且您还必须在您通过的每个字母时增加 stringposition
计数器。
要获得更高效且不太复杂的算法,请考虑将您的代码发布到 Code Review,以便进行审核,并从您获得的反馈中学习。
虽然不是最好的编码方式,但在保持代码不变的情况下,您仍然可以做一些基本的整理工作。基本上,一旦您的消息和关键字的长度相同,您就可以遍历这对字母并即时计算加密的字母,以便将其存储在一个列表中。没有理由存储中间步骤,例如将消息和关键字转换为数字列表。
if message1.isalpha() and keyword1.isalpha():
message1 = message1.upper()
keyword1 = keyword1.upper()
if len(keyword1) < len(message1):
keyword1 = keyword1 * (len(message1) / len(keyword1)) + keyword1
keyword1 = keyword1[:len(message1)]
for m, k in zip(message1, keyword1):
encrypted = ord(m) + (ord(k) - 65)
if encrypted > 90:
encrypted -= 26
encdec.append(chr(encrypted))
print ('\nThe endcoded text is:')
print (''.join(encdec))
【讨论】:
以上是关于Vigenere密码,如何处理超出字符值范围的序数值的主要内容,如果未能解决你的问题,请参考以下文章