那天,我在一个字符串里发现了幽灵

一段文本看起来毫无异常,却比预期多了几个字符;一个链接复制后再粘贴就变得无法识别。这些“毫无逻辑”的 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://ex​ample.com'; // 插入了 \u200B 零宽空格
console.log(safeUrl === fakeUrl); // false,但肉眼看不出区别

用途包括:防爬虫 / 防关键词检测、防止用户复制粘贴密码成功等。

怎么出现的?

出现的原因主要有以下几种:

  1. Unicode 标准设计的特性,Unicode 本身就定义了这些字符,目的是为了:
    1. 控制文字的排版。
    2. 实现跨语言(如阿拉伯、印地文)复杂连接形式,比如:阿拉伯、波斯等语言中,连写与不连写对语义有影响。
    3. 支持 Emoji 的组合形式(如家庭 👨‍👩‍👧‍👦)。
  2. 输入法、富文本工具自动插入
    1. 微信、Word、VSCode、富文本编辑器可能在换行或粘贴时自动插入。
    2. 搜索引擎、社交平台为了防刷或规避审查,可能加零宽字符。
  3. 有意为之(攻击、隐写、SEO)
    1. 信息隐写术:在文本中嵌入看不见的信息(类似数字水印
    2. 黑帽SEO:在关键词中隐藏零宽字符,形成伪关键词组合
    3. 网络攻击:制造“视觉钓鱼链接”
底层本质是什么? 

零宽字符其实就是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"
]

总结一句话

零宽字符虽“无形”,却能影响文本逻辑、破坏功能、甚至危及安全,开发中必须警惕它们的存在。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值