符合Python风格地实现Atbash加密

目录

1.什么是Atbash密码?

2.构造翻译表:str.maketrans()&利用str.translate()替换字符

1.签名str.maketrans(x:str,y:str,z:str = '') 

2.签名str.maketrans(x:dict[str,str],z:str = '')

3.敲代码

4.效果如何?

5.结束


刚学Python时,就觉得加密英文文本很好玩。可惜呀,技不如人,写的史山总是报错。若是你也有相应的需求,那么这篇文章将帮助你实现Atbash加密方法。本人技术不强,希望通过诙谐的文风让你学到新的Python知识或者提高对Python语言的熟悉感。

1.什么是Atbash密码?

埃特巴什(Atbash)密码是一种替换密码,它的对应法则是通过反转字母表得到的:

明文: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

密文:Z Y X W V U T S R Q P O N M L K J I H G F E D C B A

选择这种密码的好处是,比较简单。而且对密文执行同一套“加密”操作就可以得到明文,不用多写代码来解密。一眼映射!你可能想到了用字典来实现:

构造密码的对应规则atbash_dict,然后维护一个空字符串,例如rep,之后采取这样的策略:

rep = ''
for word in some_words:
    if word in atbash_dict:
        rep += atbash_dict[word]
    else:
        rep += word

这样可以完成任务,至少大部分情况下不会出错。但我打算介绍另外一种方法,它用到了内置的 字符串的translate方法和翻译表。解决方案更加具备Python风格!

2.构造翻译表:str.maketrans()&利用str.translate()替换字符

maketrans是str类的类方法,用于创建一个翻译表。这个表直接被用于字符串实例的translate方法进行字符替换操作,简直是解决这一问题的天选之子!

str.maketrans()方法可以接受以下参数构成:

str.maketrans(x:str,y:str,z:str = '')

str.maketrans(x:dict[str,str],z:str = '') 

1.签名str.maketrans(x:str,y:str,z:str = '') 

这个构造方法接受3个参数,全部是字符串,其中第三个可选。

x,y是等长字符串,分别由被替换的字符和被替换成的字符组成,彼此对应。

z是可选的,用于指定在转换时直接去除哪些字符。请看下例:

table = str.maketrans('helopytnwrd!',
                      'HELOPYTNWRD?',
                      '-_')
txt = 'hello_python-world!?'
print(txt.translate(table))
#打印:HELLOPYTHONWORLD??

在这个例子中,table把所有出现的小写字母替换成大写字母。把“!”替换为“?”,并且删除“-”和“_”。

请注意,原字符串最后的那个"?"并未被修改为“!”,所以替换是单向的。

2.签名str.maketrans(x:dict[str,str],z:str = '')

这个构造方法就是把x,y字符串替换为了字典,大家看过上例后一定能明白该怎么用。这种方法可能更灵活。不过,仍然不能整体替换好几个字符。请看下例:

dic = {'ab':'BA',
       'bc':'CB',
       'cd':'DC',}
table = str.maketrans(dic)
txt = 'abbccd'
print(txt.translate(table))
#抛出ValueError: string keys in translate table must be of length 1

并不能得到像“BACBDC”这样的结果。 

3.敲代码

程序保存至atbash.py,现在开始我们的工作。

为了构造翻译表,你可能想要像下面这样做:

table = str.maketrans('abcdefg...',
                      'zyxwvut...')

一个一个地输入,何错之有?但是可能被吐槽“太不优雅了”,那我们就换一种方式: 

lowers = ''.join(chr(x) for x in range(ord('a'),ord('z')+1))
uppers = ''.join(chr(x) for x in range(ord('A'),ord('Z')+1))
table = str.maketrans(lowers + uppers,
                      lowers[::-1] + uppers[::-1])

ord()函数接受单个字符,返回它的Unicode码点。非常棒的是,字符a-z及A-Z的码点是紧密相连的,这就为我们写生成器表达式提供了便利。

range(ord('a'),ord('z')+1)语句构造了包含a-z码点的一个可迭代对象,随后利用生成器表达式通过chr()函数获得了包含a-z字符的一个可迭代对象。

join()方法由字符串实例使用,接受一个可迭代对象,返回一个字符串。它的作用是,用字符串实例来“插入”可迭代对象的各项来生成字符串。这里实例用的是空字符串,所以可以生成由可迭代对象各项组成的字符串。

如果你不熟悉join()方法,可以参考下面的例子:

lst = ['a','b','c','d']
print('-'.join(lst))
#打印:a-b-c-d

 所以,我们拿到了一个小写字符串:'abcd...xyz',命名为lowers。再使用同样的技法拿到大写字符串:'ABCD...XYZ',命名为uppers。

直接拼接 lowers和uppers,就可以得到要被转换的字符们。至于转换结果,考虑到Atbash密码就是反转字母表,所以对两者取-1步长的完整切片再拼接就可以拿到。

我打算把它写成一个处理txt文本文件的程序,大部分功能交给一个函数,它接收一个文件名,生成加密过后的新文本文件。完整代码如下:

lowers = ''.join(chr(x) for x in range(ord('a'),ord('z')+1))
uppers = ''.join(chr(x) for x in range(ord('A'),ord('Z')+1))
table = str.maketrans(lowers + uppers,
                      lowers[::-1] + uppers[::-1])

def work_on(file_name: str)->None:
    with (open(f'{file_name}.txt',mode='r',encoding='utf-8') as f,
          open(f'atbashed_{file_name}.txt',mode='w',encoding='utf-8') as o):
        o.write(f.read().translate(table))

4.效果如何?

作为效果的检验,在同一文件夹中放入Stained,Brutal Calamity.txt(Terraria灾厄模组boss 至尊女巫,灾厄 BGM的歌词),只要在主体中添加一行代码:

work_on('Stained,Brutal Calamity')

然后运行,就可以看到文件atbashed_Stained,Brutal Calamity.txt被生成啦!我们来看看效果吧,这里只截取一部分:

原文:

Part 3 Epiphany

The end has come
For the witch and the prophecy
Deciding fates with the world on the line
Who shall emerge,
From the ashes of calamity?
How shall this fable end through these legends intertwined?

End of reasoning
Carnage and pain
A feral sense of dominance,
As all the blood melts into the rain.
A clash of history,
In blood,sweat and tears.
This war will cause our world to change,
And will be remembered through the years.
End of reasoning
Carnage and pain
A feral sense of dominance,
As all the blood melts into the rain.
A clash of history,
In blood,sweat and tears.
This war will cause our world to change,
And will be remembered through the years.

加密后的效果:

Kzig 3 Vkrkszmb

Gsv vmw szh xlnv
Uli gsv drgxs zmw gsv kilksvxb
Wvxrwrmt uzgvh drgs gsv dliow lm gsv ormv
Dsl hszoo vnvitv,
Uiln gsv zhsvh lu xzoznrgb?
Sld hszoo gsrh uzyov vmw gsilfts gsvhv ovtvmwh rmgvigdrmvw?

Vmw lu ivzhlmrmt
Xzimztv zmw kzrm
Z uvizo hvmhv lu wlnrmzmxv,
Zh zoo gsv yollw nvogh rmgl gsv izrm.
Z xozhs lu srhglib,
Rm yollw,hdvzg zmw gvzih.
Gsrh dzi droo xzfhv lfi dliow gl xszmtv,
Zmw droo yv ivnvnyvivw gsilfts gsv bvzih.
Vmw lu ivzhlmrmt
Xzimztv zmw kzrm
Z uvizo hvmhv lu wlnrmzmxv,
Zh zoo gsv yollw nvogh rmgl gsv izrm.
Z xozhs lu srhglib,
Rm yollw,hdvzg zmw gvzih.
Gsrh dzi droo xzfhv lfi dliow gl xszmtv,
Zmw droo yv ivnvnyvivw gsilfts gsv bvzih.

 当然,更复杂的文本也可以加密。比如我写的这段Python代码,里面可是有很多奇奇怪怪的,非英文的符号:

from typing import Callable,Any
from functools import wraps
from collections import defaultdict
import atexit

called_funcs = set()
counted_funcs = defaultdict(lambda :0)
def register(count:bool = False)->Callable:
    """
    注册调用的函数的装饰器,调用过后的函数会被记录下来。
    若是标记count为True,则会记录函数调用的次数。
    """
    def decorate(func: Callable)->Callable:
        @wraps(func)
        def counter(*args,**kwargs)->Any:
            name = func.__name__
            result = func(*args,**kwargs)
            if count:
                counted_funcs[f'{name}'] += 1
            called_funcs.add(name)
            return result
        return counter
    return decorate
def report():
    """在程序结束时报告记录"""
    if called_funcs:
        print(f'记录的调用函数:{called_funcs}')
    if counted_funcs:
        max_keys_len = max(map(len,counted_funcs.keys()))
        max_values_len = max(map(len,map(str,counted_funcs.values())))
        print('记录的调用次数:')
        for key,value in counted_funcs.items():
            print(f'{key:<{max_keys_len}}:{value:<{max_values_len}}')

atexit.register(report)

加密结果:

uiln gbkrmt rnklig Xzoozyov,Zmb
uiln ufmxglloh rnklig dizkh
uiln xloovxgrlmh rnklig wvuzfogwrxg
rnklig zgvcrg

xzoovw_ufmxh = hvg()
xlfmgvw_ufmxh = wvuzfogwrxg(oznywz :0)
wvu ivtrhgvi(xlfmg:yllo = Uzohv)->Xzoozyov:
    """
    注册调用的函数的装饰器,调用过后的函数会被记录下来。
    若是标记xlfmg为Gifv,则会记录函数调用的次数。
    """
    wvu wvxlizgv(ufmx: Xzoozyov)->Xzoozyov:
        @dizkh(ufmx)
        wvu xlfmgvi(*zith,**pdzith)->Zmb:
            mznv = ufmx.__mznv__
            ivhfog = ufmx(*zith,**pdzith)
            ru xlfmg:
                xlfmgvw_ufmxh[u'{mznv}'] += 1
            xzoovw_ufmxh.zww(mznv)
            ivgfim ivhfog
        ivgfim xlfmgvi
    ivgfim wvxlizgv
wvu ivklig():
    """在程序结束时报告记录"""
    ru xzoovw_ufmxh:
        kirmg(u'记录的调用函数:{xzoovw_ufmxh}')
    ru xlfmgvw_ufmxh:
        nzc_pvbh_ovm = nzc(nzk(ovm,xlfmgvw_ufmxh.pvbh()))
        nzc_ezofvh_ovm = nzc(nzk(ovm,nzk(hgi,xlfmgvw_ufmxh.ezofvh())))
        kirmg('记录的调用次数:')
        uli pvb,ezofv rm xlfmgvw_ufmxh.rgvnh():
            kirmg(u'{pvb:<{nzc_pvbh_ovm}}:{ezofv:<{nzc_ezofvh_ovm}}')

zgvcrg.ivtrhgvi(ivklig)

 这里,空代码行其实是被保留的。只不过我是写文章,把它们引用在同一个块中时自动删去了空行。代码的工作符合期望;对于使用了Atbash密码加密的文本,直接使用这段代码就可以解密。像凯撒加密就不可以,所以才选Atbash,真是很方便。

work_on('atbashed_Stained,Brutal Calamity')

然后,你就可以看到一个名为atbashed_atbashed_Stained,Brutal Calamity.txt的和Stained,Brutal Calamity.txt内容完全一样的文件,无限套娃!

5.结束

若是能帮到你,那就是我的荣幸!我用这个程序,给DeepSeek“表了个白”:

符合Python风格地实现Atbash加密_第1张图片

优雅地编写代码,看着它能够按照我预期地那样工作。突然感觉,编程真是没白学啊!

你可能感兴趣的:(python,开发语言,经验分享)