密码文本字母频率替换:比较两个字典的键值并修改文本
我看过一些类似的话题,但找不到完全符合我想要实现的解决方案。
我有一段密文,需要根据每个字母在文本中出现的频率进行简单的字母替换。我已经有一个函数可以规范化文本(变成小写,去掉非字母字符,统计字母出现的次数,然后计算每个字母的相对频率。字母作为字典的键,频率作为值。
我还有一个单独的字典,里面是字母A-Z的预期频率(k=字母,v=频率),但我对接下来该怎么做有点困惑。
我认为我需要做的是,拿着规范化后的密文、预期字母频率字典[d1]和密文字母频率字典[d2],然后像下面这样逐个处理(部分伪代码):
for word in text:
for item in word:
for k,v in d2.items():
if d2[v] == d1[v]:
replace any instance of d2[k] with d1[k] in text
decoded_text=open('decoded_text.txt', 'w')
decoded_text.write(str('the decoded text')
在这里,我想对文本说:“如果d2中的值和d1中的值匹配,就把文本中所有的d2[k]替换成d1[k]。”
我意识到我可能在基本的Python逻辑上犯了不少错误(我对Python还比较陌生),但我这样做的方向对吗?
提前谢谢你们!
更新:
感谢大家的建议。我决定尝试Karl Knechtel的方法,并对其进行了一些修改以适应我的代码。不过,我在实现上仍然遇到了一些问题。
我已经做了一个解码函数,用来处理相关的密文文件。这个函数调用了之前写的统计函数,返回一个字典(字母:频率,频率是浮点数)。这导致“制作大写版本”的代码无法正常工作,因为k和v是浮点数,不能使用.upper这个属性。所以,调用这个解码函数返回的是密文字母的频率,然后是仍然编码的密文本身。
def sorted_histogram(a_dict):
return [x[1] for x in sorted(a_dict.items(), key=itemgetter(1))]
def decode(filename):
text=open(filename).read()
cipher=text.lower()
cipher_dict=count(filename)
english_histogram = sorted_histogram(english_dict)
cipher_histogram = sorted_histogram(cipher_dict)
mapping = dict(zip(english_histogram, cipher_histogram)
translated = ''.join(
mapping.get(c, c)
for c in cipher
)
return translated
3 个回答
#!/usr/bin/env python
from operator import itemgetter
import string
def frequency(text):
d = {}
for letter in text:
try:
d[letter] += 1
except:
d[letter] = 1
return d
def alphabet():
for alpha in string.letters: yield alpha
def cipher(text):
expected = frequency(text)
flist = sorted(expected.iteritems(), key=itemgetter(1), reverse=True)
alphabet_generator = alphabet()
for char, freq in flist:
text = text.replace(char, alphabet_generator.next())
return (text, expected)
def decipher(text, expected):
nal = [ x[0] for x in sorted(expected.iteritems(), key=itemgetter(1), \
reverse=True) ]
normal_alphabet = ''.join(nal)
transtable = string.maketrans(string.letters[:len(normal_alphabet)], \
normal_alphabet)
return text.translate(transtable)
使用方法:
if __name__ == '__main__':
s = "SUMMERTIMEANDTHELIVINGISEASYFISHESJUMPING"
ciphered, expected = cipher(s)
print s
print ciphered
print decipher(ciphered, expected)
# SUMMERTIMEANDTHELIVINGISEASYFISHESJUMPING
# ciddbpjadbfekjhbnaqaegacbfcrlachbcmidoaeg
# SUMMERTIMEANDTHELIVINGISEASYFISHESJUMPING
首先要注意,频率很可能不会完全匹配,除非你的消息非常长。所以你可能需要手动调整一下,才能得到准确的消息。不过如果频率差不多的话……
你可以获取两个字典(字母)的键,并按照它们的值(频率)进行排序:
letters_in_frequency_order = sorted(d1.keys(), key=lambda x: d1[x])
然后把它们转换成字符串:
normal_alphabet = "".join(letters_in_frequency_order)
接着用这些字符串来翻译你的内容:
import string
transtable = string.maketrans(cypher_alphabet, normal_alphabet)
cyphertext.translate(transtable)
你其实不想做你现在想的事情,因为样本中的字符频率通常和参考数据中的频率分布不一致。你真正想做的是找到最常见的字符,把它替换成'e',然后是下一个最常见的字符,替换成't',依此类推。
所以我们要做的事情是:
(我假设你已经会这部分)构建一个字典,记录密文中每个字母的实际频率。
定义一个函数,这个函数接收一个{字母: 频率}的字典,并生成一个按频率排序的字母列表。
我们获取参考中的字母,按频率排序(也就是说,现在我们有了英语中最常见字母的有序列表),以及样本中的字母(同样处理)。
假设样本中最常见的字母对应英语中最常见的字母,依此类推:我们创建一个新的字典,把第一个列表中的字母映射到第二个列表中的字母。(我们也可以创建一个翻译表,用于
str.translate
。)我们会制作同一个字典的大写和小写版本(我假设你原来的字典只有小写),然后把它们合并在一起。我们使用这个映射来翻译密文,其他字符(空格、标点符号等)保持不变。
因此:
# 2.
import operator
def sorted_histogram(a_dict):
return [
x[1] # the value
for x in sorted(a_dict.items(), key=operator.itemgetter(1))
# of each dict item, sorted by value (i.e. the [1] element of each item).
]
# 3.
english_histogram = sorted_histogram(english_dict)
cipher_histogram = sorted_histogram(cipher_dict)
# 4.
# Make the lowercase version
mapping = dict(zip(english_histogram, cipher_histogram))
# Make the uppercase version, and merge it in at the same time.
mapping.update(dict(
(k.upper(), v.upper()) for (k, v) in zip(english_histogram, cipher_histogram)
))
# 5.
translated = ''.join( # make this list of characters, and string them together:
mapping.get(c, c) # the mapped result, if possible; otherwise the original
for c in cipher
)
# 6. Do whatever you want with 'translated' - write to file, etc.