一段文本看起来毫无异常,却比预期多了几个字符;一个链接复制后再粘贴就变得无法识别。这些“毫无逻辑”的 bug 背后,往往藏着一种看不见的角色——零宽字符,它,是编码世界的幽灵特工。
Unicode 背后的理念
Unicode 是一种统一字符集,它的核心目标是:“为世界上所有的语言和符号定义唯一的编码方式。”
为了处理复杂语言(如阿拉伯文、梵文、韩文、Emoji等)中的排版规则、字符组合、连接方式,Unicode 引入了许多控制性字符,其中就包括 零宽字符(Zero Width Characters)。
什么是零宽字符?
零宽字符是一类在文本中占据位置但不显示内容的字符。它们不会在页面或控制台中被直接看到,却确实存在于文本中,对字符串操作(如长度、匹配、复制)等都产生实际影响。
就像幽灵一样,你看不到它,但它确实在场 —— 所以也叫做“幽灵字符”。
常见零宽字符及用途
名称 | Unicode | 符号 | 常见用途 |
---|---|---|---|
零宽空格(ZWSP) | U+200B | \u200B | 控制换行、分词,不显示内容 |
零宽非连接符(ZWNJ) | U+200C | \u200C | 禁止两个字母连接(如阿拉伯语) |
零宽连接符(ZWJ) | U+200D | \u200D | 强制字符连写(用于 Emoji 联合) |
零宽无断空格(NBSP) | U+FEFF | \uFEFF | 旧时作为 BOM 标记,现在也用作零宽空格 |
举例说明
1、零宽字符影响字符串长度
const a = 'abc';
const b = 'a\u200Db\u200Dc';
console.log(a === b); // false
console.log(a.length); // 3
console.log(b.length); // 5
目前肉眼看到长度不一样,但要是这样呢?
是不是很神奇呢!
2、零宽字符用于 Emoji 联合显示
// 👨👩👧👦 是由 4 个 Emoji + 零宽连接符 ZWJ (U+200D) 组合而成
const family = '👨\u200D👩\u200D👧\u200D👦';
console.log(family); // 👨👩👧👦
console.log(family.length); // 实际是 7 个字符
'👩\u200D❤️\u200D👩' = '👩❤️👩'
3、被用来隐写、投毒攻击、钓鱼
const safeUrl = 'https://example.com';
const fakeUrl = 'https://example.com'; // 插入了 \u200B 零宽空格
console.log(safeUrl === fakeUrl); // false,但肉眼看不出区别
用途包括:防爬虫 / 防关键词检测、防止用户复制粘贴密码成功等。
怎么出现的?
出现的原因主要有以下几种:
- Unicode 标准设计的特性,Unicode 本身就定义了这些字符,目的是为了:
- 控制文字的排版。
- 实现跨语言(如阿拉伯、印地文)复杂连接形式,比如:阿拉伯、波斯等语言中,连写与不连写对语义有影响。
- 支持 Emoji 的组合形式(如家庭 👨👩👧👦)。
- 输入法、富文本工具自动插入
- 微信、Word、VSCode、富文本编辑器可能在换行或粘贴时自动插入。
- 搜索引擎、社交平台为了防刷或规避审查,可能加零宽字符。
- 有意为之(攻击、隐写、SEO)
- 信息隐写术:在文本中嵌入看不见的信息(类似数字水印)
- 黑帽SEO:在关键词中隐藏零宽字符,形成伪关键词组合
- 网络攻击:制造“视觉钓鱼链接”
底层本质是什么?
零宽字符其实就是Unicode 中的特殊字符,有明确的码位(code point),但在渲染时宽度为 0,也就是“不可见”但“可存在”的字符。
底层本质:
- 本质是一个字符(char),拥有 Unicode 编码,如 \u200B。
- 它不对应任何可视图形,因此渲染宽度为零。
- 在字符串处理中,它与其他字符一样被当作“合法字符”处理。
渲染原理与底层机制
层级 | 作用 |
---|---|
Unicode 标准 | 定义零宽字符的含义、码点、行为。 |
字体系统(font rendering) | 零宽字符没有对应的图形(glyph),所以渲染为空宽。 |
渲染引擎(如浏览器的 Blink) | 遇到这些字符时,会根据规则影响排版(如连写或断词)。 |
字符串处理(如 JS) | 会计算在 .length 中,会参与匹配、截取等操作。 |
🌰
'好'.length // 1
'好\u200B'.length // 2,尽管看不见
'好\u200B'.charCodeAt(1) // 8203,正是 U+200B
如何检测和清理?
1、JavaScript 中去除零宽字符:
const clean = str => str.replace(/[\u200B-\u200D\uFEFF]/g, '');
2、在线检测工具
地址:Zero-width character detector
3、VSCode/编辑器插件:Highlight Bad Chars
安装插件后,在设置中添加想要高亮的字符,比如:
"highlight-bad-chars.custom": [
"\\u200B",
"\\u200C",
"\\u200D",
"\\uFEFF"
]
总结一句话
零宽字符虽“无形”,却能影响文本逻辑、破坏功能、甚至危及安全,开发中必须警惕它们的存在。