什么是零宽字符
所谓零宽字符,就是不可见的「非打印」字符,通过视觉无法看出字符串中是否有零宽字符,但是通过代码遍历,是可以获取到该字符的。有些软件也可以将零宽字符打印出来。
常见的零宽字符有:
零宽空格(zero-width space, ZWSP)用于可能需要换行处。
Unicode: U+200B HTML: ​
零宽不连字 (zero-width non-joiner,ZWNJ)放在电子文本的两个字符之间,抑制本来会发生的连字,而是以这两个字符原本的字形来绘制。
Unicode: U+200C HTML: ‌
零宽连字(zero-width joiner,ZWJ)是一个控制字符,放在某些需要复杂排版语言(如阿拉伯语、印地语)的两个字符之间,使得这两个本不会发生连字的字符产生了连字效果。
Unicode: U+200D HTML: ‍
左至右符号(Left-to-right mark,LRM)是一种控制字符,用于计算机的双向文稿排版中。
Unicode: U+200E HTML: ‎ ‎ 或‎
右至左符号(Right-to-left mark,RLM)是一种控制字符,用于计算机的双向文稿排版中。
Unicode: U+200F HTML: ‏ ‏ 或‏
字节顺序标记(byte-order mark,BOM)常被用来当做标示文件是以UTF-8、UTF-16或UTF-32编码的标记。
Unicode: U+FEFF
零宽字符有什么用
隐藏信息
零宽字符可以用来隐藏和加密一些信息。
例如我想在一条链接中带上隐形水印LvLin
,可以先将LvLin
转成二进制字符串:
let name = 'LvLin';
// 将 name 进行字符拆分,然后每个字符进行转译
function textToBinary(name) {
return name.split('').map(char => {
return char.charCodeAt(0).toString(2);
})
}
let binaryArr = textToBinary(name);
// ['1001100', '1110110', '1001100', '1101001', '1101110']
将二进制字符串换成零宽字符替代:
function binaryToZeroWidth (binary) {
return binary.split('').map((binaryNum) => {
if (+binaryNum === 1) {
return '•'; // zero-width space
// '•'.charCodeAt(0).toString(16) => '200b'
} else {
return '•'; // zero-width non-joiner
// '•'.charCodeAt(0).toString(16) => '200c'
}
}).join('');
}
let zeroWidthArr = binaryArr.map(binaryToZeroWidth);
将零宽字符拼接在链接后面:
let link = 'lvalue.com';
link = link + zeroWidthArr.join('•') // zero-width joiner
// '•'.charCodeAt(0).toString(16) => '200d'
// link.length => 49
这样我们就获取到了一个带有隐形水印的链接:lvalue.com
。
提取隐藏信息
将零宽字符从链接中提取出来,转成二进制数,再转成字符串。
// 提取隐藏信息
let zeroWidthArr = link.replace(/[^\u200b-\u200d]/g, "").split('•'); // zero-width joiner
// 将零宽字符串转成二进制数,再转成字符
function zeroWidthToStr (zeroWidthStr) {
let binaryStr = zeroWidthStr.split('').map((zeroWidthChar) => {
if (zeroWidthChar === '•') {
return '1';
} else {
return '0';
}
}).join('');
return binaryToStr(+binaryStr)
}
// 将二进制数转成字符
function binaryToStr(binary) {
return String.fromCharCode(parseInt(binary, 2));
}
let myName = zeroWidthArr.map(zeroWidthToStr).join('');
console.log({myName}) // 'LvLin'
常用操作
过滤零宽字符
newStr = str.replace(/[\u200b-\u200f\uFEFF\u202a-\u202e]/g, "");
提取零宽字符
newStr = str.replace(/[^\u200b-\u200f\uFEFF\u202a-\u202e]/g, "");