零宽字符的 python 实现
原理
零宽字符
零宽字符是一种在浏览器中不打印的字符,大致相当于 display: none ,在许多文本应用中也不显示,比如邮箱、QQ、微信、文本编辑器等
这里有三种零宽字符 – 零宽空格、零宽连字、零宽不连字
零宽字符在浏览器中对应的转义字符
零宽空格 --- ​
零宽不连字 --- ‌
零宽连字 --- ‍
摩斯电码
摩斯电码采用长短两种符号进行文本加密,通过字典进行加密和解码,摩斯电码字典如下图所示:
字符 | 电码符号 | 字符 | 电码符号 | 字符 | 电码符号 | 字符 | 电码符号 |
---|---|---|---|---|---|---|---|
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 | –.. |
由于摩斯码只有 -
和 .
两个符号,而两个字母之间没有区分,我们假设用 /
区分。
那么 test
就变成 t/e/s/t
,接着就是 -/./.../-
。根据三种不同的零宽字符,我们再将 / 替换为零宽空格; - 替换问零宽连字;. 替换为零宽不连字。
/ --> ​
. --> ‌
- --> ‍
那么 -/./.../-
就变成了 ‍​‌​‌‌‌​‍
。
将这段零宽字符粘贴进一个HTML文件当中
<p>前</p>
<div>‍​‌​‌‌‌​‍</div>
<p>后</p>
在浏览器中打开这个HTML文件,你只能看到 “前后” 两个字,到这了原理已经可见一斑了。
python 实现
创建密码字典
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 "
morse_codes = ['.-','-...','-.-.','-..','.','..-.','--.','....','..','.---','-.-','.-..','--','-.','---','.--.','--.-','.-.','...','-','..-','...-','.--','-..-','-.--','--..','-----','.----','..---','...--','....-','.....','-....','--...','---..','----.','-...-']
encode_dict = dict(zip(chars.lower(), morse_codes))
decode_dict = dict(zip(morse_codes, chars.lower()))
加密函数
简单粗暴,将字符串看成字符,每个字符转义为摩斯码,并且中间加 “/”,然后对每一个摩斯码符号(最小的 .
,-
,/
)转义为零宽字符。
def string_2_nonezero(s):
def char_2_morse(char):
return encode_dict.get(char, "-...-")
def string_2_nonezero(string):
change_dict = dict(zip(["/", ".", "-"], ["​", "‌", "‍"]))
return "".join([change_dict.get(s, " ") for s in string])
morse = "/".join([char_2_morse(x) for x in s])
return string_2_nonezero(morse)
解密函数
首先从待解密字符串中匹配零宽字符, 零宽字符在Unicode中的编码为 \u200B \u200C \u200D| 在HTML中有两种显示
​
‌ --> &zwnj
‍ --> &zwj
- 匹配出文本中的零宽字符
- 转换零宽字符为摩斯字符串
- 调用解密字典把摩斯码转换普通文本
import re
def nonezero_2_string(s):
nonezero_string = re.match(r"(​|‌|‍|\u200B|\u200C|\u200D|‌|‍)+", s).string
nonezero_string = re.sub(r"​|\u200B", "/", nonezero_string)
nonezero_string = re.sub(r"‌|\u200C|‌", ".", nonezero_string)
nonezero_string = re.sub(r"‍|\u200D|‍", "-", nonezero_string)
return "".join([decode_dict.get(x, " ") for x in nonezero_string.split("/")])
参考
完整代码
import re
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 "
morse_codes = ['.-','-...','-.-.','-..','.','..-.','--.','....','..','.---','-.-','.-..','--','-.','---','.--.','--.-','.-.','...','-','..-','...-','.--','-..-','-.--','--..','-----','.----','..---','...--','....-','.....','-....','--...','---..','----.','-...-']
encode_dict = dict(zip(chars.lower(), morse_codes))
decode_dict = dict(zip(morse_codes, chars.lower()))
def string_2_nonezero(s):
def char_2_morse(char):
return encode_dict.get(char, "-...-")
def string_2_nonezero(string):
change_dict = dict(zip(["/", ".", "-"], ["​", "‌", "‍"]))
return "".join([change_dict.get(s, " ") for s in string])
morse = "/".join([char_2_morse(x) for x in s])
return string_2_nonezero(morse)
def nonezero_2_string(s):
nonezero_string = re.match(r"(​|‌|‍|\u200B|\u200C|\u200D|‌|‍)+", s).string
nonezero_string = re.sub(r"​|\u200B", "/", nonezero_string)
nonezero_string = re.sub(r"‌|\u200C|‌", ".", nonezero_string)
nonezero_string = re.sub(r"‍|\u200D|‍", "-", nonezero_string)
return "".join([decode_dict.get(x, " ") for x in nonezero_string.split("/")])
h = "hello"
print(h)
print(string_2_nonezero(h))
print(nonezero_2_string(string_2_nonezero(h)))