#!/usr/bin/python3
# 文件名: 字符串加密
# 作者:巧若拙
# 时间:2019-1-25
'''描述:字符串加密。待加密的n个字符(仅由ASCII码字符构成,最多支持960个字符),加密方式如下:
①产生一个3到6之间的随机整数k, 将十进制数960均分成k份,字符在字符串中的位置除以k的余数决定该字符存放在第几份数据中(余数为1保存在第一份数据中,余数为2保存在第二份数据...余数为0保存在第k份数据中);
②用十进制数127减去每个字符的ASCII码值,得到的差作为该字符的密文,同一段内的密文依次存放;
③将随机产生的数k加64后作为第一个密文存放到数组b中;
④将其他所有有密文按照在数据段中的位置依次逆序存放到数组b中;
⑤将数组b中的每个密文用3位数字保存,不足3位的前面用0补足,然后依次连接成一个新的字符串;
⑥返回密文字符串。
函数名:encrypt (p)
参数表:p –– 待加密的字符串。
返回值:c –– 加密后的字符串。
例1,当p = ”zp123”时,c=” 067078076015077005”;
例2,当p = ”Vb”时,c=” 068029041”。
'''
import random
def encrypt(p):
k = random.choice((3, 4, 5, 6)) #3到6之间的随机整数k
k = 4
a = [-1] * 961 #默认均为-1
t = 960 // k
for i in range(len(p)):
r, c = i % k, i // k
a[r*t+c] = 127 - ord(p[i]) #将密文存储到数组a中
a[960] = k + 64
s = ['0'*(3-len(str(i))) + str(i) for i in a[::-1] if i != -1]
return "".join(s)
'''
解密是加密的逆过程。首先从前3个字符中计算出总行数k,然后每3个字符一组,
把所有的明文字符都翻译出来,依次存储到列表a,再把a翻转,得到顺序排列的明文字符。需要注意的是此时的明文字符是按照行序号排列的,我们需要将其转换成按列序排列才能得到最终的结果。例如,若密文字符串s=” 068071075072076079073077070074078”;解密得到k=4,a= ['1', '5', '9', '2', '6', '0', '3', '7', '4', '8']。将其排列到二维数组中,就是:
把明文字符从二维数组中按照列序依次存储到列表p中并转化为字符串,
就得到最终结果”1234567890”。
因为二维数组的最后一列不一定能填满,
为了把列表a的元素存储到二维数组中的正确位置,
我们需要引入两个变量width和rest,其中width = len(a) // k表示最小宽度,
即已经装满的列数rest = len(a) % k表示未满列的行数。若所有的列都是满的,
则width等于总列数,rest=0。
设置好这两个变量以后,我们就可以按照行序把列表a中的元素依次填入二维数组,
再按照列序把元素从二维数组中取出来,就得到了正确排列的明文字符。
'''
#解密算法1:构造一个二维数组,思路较为直观
def decrypt(s):
k = int(s[:3]) - 64 #总共k行
a = []
for i in range(3,len(s),3):
a.append(chr(127 - int(s[i:i+3])))
a = a[::-1]
print(a)
width = len(a) // k #最小宽度,当所有的列都满时,刚好等于列数
rest = len(a) % k #数据未满列也有数据的行数
b = [[-1 for i in range(width+1)] for j in range(k)]
c, r, i = 0, 0, 0
while i < len(a): #按照行序存储明文到二维数组中
if c < width or (c == width and r < rest):
b[r][c] = a[i]
c += 1
i += 1
else: #到了右边界,处理下一行
r += 1
c = 0
p = [] #按列序从二维数组提取明文
for c in range(width): #先提取排满数据的列
for r in range(k):
p.append(b[r][c])
for r in range(rest): #再提取未排满数据的列
p.append(b[r][width])
return "".join(p)
#解密算法2:利用移动数组a的下标,直接生成明文,代码简洁但不够直观
def decrypt2(s):
k = int(s[:3]) - 64 #总共k行
a = []
for i in range(3,len(s),3):
a.append(chr(127 - int(s[i:i+3])))
a = a[::-1]
width = len(a) // k #最小宽度,当所有的列都满时,刚好等于列数
rest = len(a) % k #数据未满列也有数据的行数
p = [] #按列序从二维数组提取明文
for c in range(width): #先提取排满数据的列
i = c #a的下标指向第一行的下一列位置
for r in range(k):
p.append(a[i])
#a的下标向后跳跃,移动到其在矩阵中的正下方的位置
if r < rest:
i += width + 1
else:
i += width
i = width #再提取未排满数据的列
for r in range(rest):
p.append(a[i])
i += width + 1 #跳到下一行
return "".join(p)
p = "1234567890"
s = encrypt(p)
print(s)
print(decrypt(s))
print(decrypt2(s))