CV工程师
2022-10-29 07:44:30 阅读:2962
这周做了一个有意思的小工具:文字里的秘密,这个小工具可以把一段文字隐藏于另一段文字之间。例如你有两段文字:
但是我们程序员嘛,都是比较含蓄的,怎么能直接就表达出来呢?于是我们就用这个工具把 我喜欢你
因此到她的名字当中,因此后我们看到的名字还是 赵丽颖
,这个时候我们把隐藏后的名字发送给她,让她在我们的工具中解密,就能看到我们委婉表达的意思了。
解密:
加密:
是不是看起来非常炫酷,事实上,我觉得这是一个非常炫酷的小工具,那么它的原理是什么呢?
其实可能大家之前有接触过一个叫做 游戏空白名 的东西,在游戏里你的名字显示空白。但是明明人家游戏已经限制了名字不能为空,为什么还可以设置为“空”呢?其实还有一种可能,就是名字并不是空的,仅仅是有些文字我们看不见而已。它的名字就叫做:零宽字符
零宽度字符 是一些不可见的,不可打印的字符。它们存在于页面中主要用于调整字符的显示格式。零宽字符有以下几种:
也就是说一共有六种字是我们看不见的,因此我们只需要把这些字符随机打进游戏昵称里,理论上就能实现空白名的效果。
说了这么多,这个和我们的这个小工具有什么关系呢?如果非要有关系就一定要把我们的普通文字转换成上面这些所谓的零宽字符。其实就是这样,我们只需要把普通文字按照一定的格式转换成零宽字符,就可以隐藏这段文字,随后我们再将零宽字符按照我们的格式还原,就可以把隐藏的文字显示出来了。
知道原理以后我们的开发工作就相对很简单了。首先我们要解决的问题就是如何把文字和零宽字符对应的?至少中文常用字都两千多个了,可是零宽字符只有六个,想到这里,就有了思路。计算机能够显示这么多东西,还不是因为把所有的内容都转换成了二进制吗?既然如此,我们就可以首先将文字转换成二进制,然后仅需要用到两个零宽字符(一个代表0,一个代表1)就可以了。
然而事实上,我们需要把一个一个的文字分开,于是还需要第三个字符:分隔符.
enum ZWSP {
ZERO = '\u200e',
ONE = '\u200f',
SPLIT = '\u200c',
}
其他就是相互转换的问题了,具体代码如下:
/**
* [ZWSP:零宽空格](https://zh.wikipedia.org/wiki/%E9%9B%B6%E5%AE%BD%E7%A9%BA%E6%A0%BC)
*/
enum ZWSP {
ZERO = '\u200e',
ONE = '\u200f',
SPLIT = '\u200c',
}
const CODE_MAP = {
'0': ZWSP.ZERO,
'1': ZWSP.ONE,
};
const CODE_MAP2 = {
[ZWSP.ONE]: '1',
[ZWSP.ZERO]: '0',
};
/**
* 转换字符串为二进制字符串数组
*/
const str2binary = (str: string) => {
if (!str.length) return [];
const binaryMap = str.split('').map((c) => c.charCodeAt(0).toString(2));
return binaryMap;
};
/**
* 转换二进制字符串数组为字符串
*/
const binary2str = (bArr: string[]) => {
const cArr = bArr.map((b) => String.fromCharCode(parseInt(b, 2)));
return cArr.join('');
};
/**
* 转换二进制字符串数组为零宽字符串
*/
const covertBinary2ZWSP = (barr: string[]) => {
const covertSingle = (binaryStr: string) => {
if (!binaryStr.length) return '';
return binaryStr
.split('')
.map((c) => CODE_MAP[c])
.join('');
};
const str = barr.map(b => covertSingle(b)).join(ZWSP.SPLIT);
return str;
};
/**
* 转换零宽字符串回二进制字符串数组
*/
const ZWSP2Binary = (zwspStr: string) => {
const cArr = zwspStr.split(ZWSP.SPLIT);
console.log(cArr);
const covertSingle = (zwspC: string) => {
if (!zwspC.length) return '';
return zwspC
.split('')
.map((c) => CODE_MAP2[c])
.join('');
};
return cArr.map(covertSingle);
};
const encode = (string: string) => covertBinary2ZWSP(str2binary(string));
const decode = (encodeStr: string) => binary2str(ZWSP2Binary(encodeStr));
/**
* `zwsp.encode` 转换明文为零宽字符
*
* `zwsp.decode` 转换零宽字符为明文
*/
const zwsp = {
encode,
decode,
};
export default zwsp;
最初的版本发现一个问题:手机端复制加密后的文字发送给好友之后并不难成功转换,后来发现是手机端微信过滤掉了 \u200b
这个字符,随后更换了一个零宽字符得以解决。
为了防止在复制时漏掉我们看不见的零宽字符,我将零宽字符放在了明文的第一个字后面,然后在手机端的微信中会在第一个字符后显示一个空格出来,当时考虑到并不是多么的影响,因此就这样先用着吧。
灵感以及代码来源于空山。
评论
扫描二维码获取文章详情
更多精彩内容尽在:WWW.ZNGG.NET