设备场景
波斯文里的零宽非连接符是正常文字,放进标识符时为什么要另设边界
波斯文中的零宽非连接符并非可随手删除的隐藏字符。本文从自然书写、Unicode 标识符规则、国际化域名上下文检查和浏览器 URL 处理四层拆解:什么时候它承担真实的断连作用,什么时候必须对入口身份另做核验。
看起来只差一点,底层可能不是同一串字符
同一段波斯文放在正文、搜索结果和网址栏里,连写效果可能不同。最容易出现的误判有两种:一种认为系统吞掉了字符,另一种看到“不自然的断开”就断定入口被冒充。问题在于,屏幕上的字形不是完整证据。波斯文从右向左排列,字母还会根据相邻关系改变形态;其中 U+200C ZERO WIDTH NON-JOINER,中文常称零宽非连接符或零宽不连字符,不占一个可见空格,却会阻止两侧字母继续连写。
因此,一个词肉眼看起来只多了一处断开,实际字符序列可能多了 U+200C;反过来,不同字体、排版引擎或复制路径,也可能让同一序列呈现得略有差异。第一步不该是猜测“少了哪个字”,而是确认文本中是否存在连接控制符。文字显示与入口身份是两个问题,混在一起处理,往往既破坏波斯文,也没有完成安全核验。
正常书写为什么会需要一个看不见的符号
Unicode 标识符规范 UAX #31 把连接控制符列入会影响显示的默认可忽略字符,同时承认它们用于某些语言的正字法。这个并列关系很重要:所谓“默认可忽略”,不等于在自然语言里没有意义。ZWNJ 的作用不是插入空白,而是在字符仍保持相邻时改变草写连接。对波斯文词形、词缀边界或复合结构而言,这种断连可能正是作者要表达的写法。
如果清洗程序把所有不可见字符统一删除,两侧字母可能重新连接,词的视觉结构也随之改变。若程序把它统一替换成普通空格,又可能把一个词拆成两个检索单元。搜索、分词、排序和去重于是得到不同结果。看似“清理乱码”的动作,实际可能改写内容。
但这也不意味着任何位置出现 U+200C 都应被接受。Unicode UTS #39 明确指出,完全禁用 Join_Control 会妨碍部分语言正确呈现常用词;与此同时,允许 U+200C 和 U+200D 应限定在规定上下文,而且这些限制仍不能消除所有视觉混淆。合理做法不是二选一,而是先回答它在这段语言文字中是否有正当作用,再回答当前系统是否允许这种作用进入标识符。
正文和标识符追求的不是同一件事
正文的首要目标是忠实表达。只要字符序列符合语言习惯,编辑器通常应保留它,让字体与排版引擎完成显示。域名、账号、文件键或程序变量则承担“识别同一个对象”的任务。系统需要稳定比较,还要防止两个不同序列呈现得过于相似。一个在正文中必要的显示控制,到了标识符里便需要更窄的边界。
UAX #31 因此建议通用标识符配置在没有特定需要时排除连接控制符,并提醒不可见字符、视觉相同字符和双向重排都会带来欺骗问题。这里的重点不是给某个字符贴上“危险”标签,而是要求标识符制定明确的配置:允许哪些字符、在哪些位置允许、比较时如何规范化。以及显示形式是否与内部比较形式不同。
可以把两种场景作一个受控比较。段落里的波斯文若删除 ZWNJ 后连写发生变化,说明它可能参与词形表达,应回到语言上下文判断;网址标签里的 ZWNJ 即使让词形更自然,也还要通过 IDNA 的上下文规则。前者问“这句话写得对不对”,后者问“这串代码点能否稳定指向一个主机”。答案可能同时为真,也可能一真一假。
CONTEXTJ 是条件许可,不是自由通行证
IETF 的 RFC 5892 为国际化域名定义代码点属性。U+200C 与 U+200D 被标为 CONTEXTJ,也就是必须有上下文规则。规范明确要求:若没有建立相应规则,或字符所在位置不符合规则,包含它的字符串不能用于注册,甚至不能用于查询。
RFC 5892 的 U+200C 附录直接提到波斯文:在阿拉伯字母这类草写系统中,ZWNJ 可按正字法要求断开连接。规则随后检查它是否处于合适的连接类型之间,另有用于印度文字 virama 上下文的条件。换句话说,标准没有否定波斯文需求,而是把“语言确实需要”落实为可以计算的前后文约束。
这层约束带来两个结论。第一,看到地址里存在 ZWNJ,不能立刻断言它是假网址;字符可能处在有效的波斯文上下文。第二,通过 CONTEXTJ 也不能证明网站就是某个品牌或机构。它只表明这个标签在字符层面符合 IDNA 条件,不能替代证书、目标主机、官方公布入口和运营主体的核验。

地址栏显示仍然不是最终身份凭证
浏览器不会把用户输入的主机名原样当作普通文字。WHATWG URL Standard 规定,域名 ToASCII 处理会启用 CheckJoiners 和 CheckBidi;严格解析失败时,域名可被判为无效。浏览器显示国际化域名时,又可能把内部域名转回 Unicode 形式。于是用户看到的波斯文字形、程序用于联网的 ASCII 形式以及最初复制的字符串,可能处在不同表示层。
规范还特别提醒,国际化域名、特殊字符和双向文本需要防范同形欺骗。这里不能推导成“Unicode 域名都不安全”,只能说明视觉相似不够作为身份依据。一个链接的可见文字可以写成正常波斯文,实际 href 却指向另一主机;地址栏也可能因安全策略显示 Unicode 或 ASCII/Punycode。真正应比较的是浏览器解析出的 host,而不是聊天消息里那段可见标签。
对普通读者而言,没有必要手算 IDNA。重要的是建立分层检查:文字层保留原始序列,避免先破坏正字法;标识符层查看代码点和规范化结果;入口层确认协议、完整主机名、端口与实际跳转。只有第三层回答“我将连接到哪里”。
一套不破坏文字的检查顺序
遇到同一词在正文和网址里显示不同,可以按以下顺序处理。
首先复制原始字符串到能够显示 Unicode 代码点的工具,记录 U+200C 的准确位置,不要先执行“删除全部零宽字符”。同时保留一份原文,避免后续比较失去基准。
其次把正文与标识符分开。正文检查删除或保留 U+200C 是否改变波斯文连接形态和词边界;网址则交给标准 URL 解析器,读取解析后的 hostname,并同时记录 Unicode 显示与 ASCII 形式。不要用字符串截取或从右到左的肉眼顺序自行拆主机名。
再次核对实际链接目标。悬停或检查 href,确认主机名的点号边界、子域层级和顶级域;若发生跳转,记录最终主机。来自搜索结果、短消息或二维码的标签,都不能只凭可见文字认定归属。

最后才决定处置。语言上下文合理、域名解析通过且入口来源可核验,可以保留;语言上下文不明时先请熟悉波斯文的人复核;解析失败、主机不符或跳转异常时停止访问。不要把“存在 ZWNJ”单独当作封禁条件,也不要把“显示得像官方名称”当作放行条件。
边界:规则验证字符串,不替网站背书
零宽非连接符横跨自然语言与机器标识符,争议恰恰来自两边都只看了一半。把它全部删掉,会损害正常波斯文;把正文中的正当用途直接搬到域名,又忽略了不可见差异对稳定比较和防混淆的影响。
可靠结论应保持克制:U+200C 可以是正常文字的一部分,标识符也可以在严格上下文中允许它;但任何一条字符规则都不能证明入口主体可信。先保护文字,再检查代码点,最后核对解析后的真实主机,才能同时守住语言准确性与入口安全。
搜索与账号系统还要保存比较证据
同一原则也适用于站内搜索和账号系统。若系统把 ZWNJ 纳入可用标识符,就应公开采用的 Unicode 版本、规范化方式和允许上下文,并在注册与登录时使用同一套比较规则。若搜索只想提高召回率,可以另建忽略连接控制符的检索键,但结果页仍应显示原文,不能用检索键覆盖作者输入。这样既能找到写法略有差异的内容,也不会把两个原始序列悄悄改成同一个展示文本。
日志与客服记录应保存两种表示:一份是用户看到的 Unicode 字符串,另一份是系统真正解析或比较的形式。若只截一张画面,后续无法确认差异来自字符、字体还是重排;若只保存 ASCII 结果,又会失去波斯文书写线索。成对记录能让语言复核与安全排查各自获得所需证据。
资料来源
- Unicode Consortium:《Unicode Standard Annex #31: Unicode Identifiers and Syntax, Version 17.0.0》,发布或更新于 2025-08-20
- Unicode Consortium:《Unicode Technical Standard #39: Unicode Security Mechanisms, Version 17.0.0》,发布或更新于 2025-08-20
- IETF / RFC Editor:《RFC 5892: The Unicode Code Points and Internationalized Domain Names for Applications (IDNA)》,发布或更新于 2010-08-01
- WHATWG:《URL Standard》,发布或更新于 2026-07-06