零宽字符的 python 实现

2019/11/06 Python Html

零宽字符的 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>&#8205;&#8203;&#8204;&#8203;&#8204;&#8204;&#8204;&#8203;&#8205;</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(["/", ".", "-"], ["&#8203;", "&#8204;", "&#8205;"]))
        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中有两种显示

&#8203;
&#8204;   -->  &zwnj
&#8205;   -->  &zwj
  1. 匹配出文本中的零宽字符
  2. 转换零宽字符为摩斯字符串
  3. 调用解密字典把摩斯码转换普通文本
import re

def nonezero_2_string(s):
    nonezero_string = re.match(r"(&#8203;|&#8204;|&#8205;|\u200B|\u200C|\u200D|&zwnj;|&zwj;)+", s).string
    nonezero_string = re.sub(r"&#8203;|\u200B", "/", nonezero_string)
    nonezero_string = re.sub(r"&#8204;|\u200C|&zwnj;", ".", nonezero_string)
    nonezero_string = re.sub(r"&#8205;|\u200D|&zwj;", "-", 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(["/", ".", "-"], ["&#8203;", "&#8204;", "&#8205;"]))
        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"(&#8203;|&#8204;|&#8205;|\u200B|\u200C|\u200D|&zwnj;|&zwj;)+", s).string
    nonezero_string = re.sub(r"&#8203;|\u200B", "/", nonezero_string)
    nonezero_string = re.sub(r"&#8204;|\u200C|&zwnj;", ".", nonezero_string)
    nonezero_string = re.sub(r"&#8205;|\u200D|&zwj;", "-", 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)))

Search

    Table of Contents