零宽空格 U+200B 引发的问题及扩展

发布于 2023-05-13 14:24:41 字数 5013 浏览 70 评论 0

配图源自 Freepik

是这样的,最近在写一个微信公众号的处理脚本,用来替换替换文章中的指定内容。

function getInsertElement(rootElement) {
  const matchFlag = 'AA'
  const pList = [...rootElement.querySelectorAll('p')]
  let matchedElement = pList.find(el => {
    const text = (el.textContent || el.innerText).replace(/\u00a0/gi, '').trim()
    return text === matchFlag
  })
  return matchedElement
}

上面的方法是脚本的一部分,用于获取文章中指定字符串所在的 DOM 元素,思路是通过 Node.textContent 来匹配的。在调试的时候,发现有时候匹配不上,用「肉眼」看是没问题的,但硬是匹配不上。经过一番排查之后,发现了一个有趣的事情,如图:

在编辑器内有字符 AA,然后使用 encodeURIComponent($0.textContent) 的编码结果是 AA%E2%80%8B,所以上面 text === matchFlag 比较结果为 false,自然就匹配不上了。下面将其转换为 Unicode 字符:

function string2Unicode(str) {
  return str
    .split('')
    .map(value => {
      const temp = value.charCodeAt(0).toString(16).padStart(4, '0').toUpperCase()
      if (temp.length > 2) return '\\u' + temp
      return value
    })
    .join('')
}

const encodedStr = '%E2%80%8B'
const originString = decodeURIComponent(encodedStr)
const unicodeStr = string2Unicode(originString)
console.log(unicodeStr) // \u200B

转换得出 %E2%80%8B 的 Unicode 编码为 U+200B,然后查询这里发现它是「零宽空格」,是一种不可见的字符。因此,只要使用正则表达式 /\u200b/gi,把所有零宽空格干掉就行了。

function getInsertElement(rootElement) {
  const matchFlag = 'AA'
  const pList = [...rootElement.querySelectorAll('p')]
  let matchedElement = pList.find(el => {
    const text = (el.textContent || el.innerText)
      .replace(/\u00a0/gi, '')
      .replace(/\u200b/gi, '')
      .trim()
    return text === matchFlag
  })
  return matchedElement
}

零宽空格

零宽空格(zero-width space,ZWSP)是一种不可见、不可打印的 Unicode 字符,用于可能需要换行处。

在 Unicode 中,该字元为 U+200B。在 HTML 中转义字符有:​​​。一般情况下,它是不可见的,但一些软件对这些不可见字符做了处理,视觉上可感知。举个例子:

相邻单词之间有一个零宽空格

LoremIpsumDolorSitAmetConsecteturAdipiscingElitSedDoEiusmodTemporIncididuntUtLaboreEtDoloreMagnaAliquaUtEnimAdMinimVeniamQuisNostrudExercitationUllamcoLaborisNisiUtAliquipExEaCommodoConsequatDuisAuteIrureDolorInReprehenderitInVoluptateVelitEsseCillumDoloreEuFugiatNullaPariaturExcepteurSintOccaecatCupidatatNonProidentSuntInCulpaQuiOfficiaDeseruntMollitAnimIdEstLaborum

相邻单词之间无零宽空格

LoremIpsumDolorSitAmetConsecteturAdipiscingElitSedDoEiusmodTemporIncididuntUtLaboreEtDoloreMagnaAliquaUtEnimAdMinimVeniamQuisNostrudExercitationUllamcoLaborisNisiUtAliquipExEaCommodoConsequatDuisAuteIrureDolorInReprehenderitInVoluptateVelitEsseCillumDoloreEuFugiatNullaPariaturExcepteurSintOccaecatCupidatatNonProidentSuntInCulpaQuiOfficiaDeseruntMollitAnimIdEstLaborum

它们在 VS Code 及页面中的展示效果,如图所示:

扩展

除此之外,还有零宽连字、零宽不连字也是不可见字符。

  • 零宽连字(zero-width joiner,ZWJ)是一个控制字符,放在某些需要复杂排版语言(如阿拉伯语、印地语)的两个字符之间,使得这两个本不会发生连字的字符产生了连字效果。其 Unicode 编码为 U+200D,HTML 转义字符有:&#8205&zwj

  • 零宽不连字(zero-width non-joiner,ZWNJ)是一个不打印字符,放在电子文本的两个字符之间,抑制本来会发生的连字,而是以这两个字符原本的字形来绘制。其 Unicode 编码为U+200C,HTML 转义字符为:‌

相信你也看过网友「把幸福的一家几口强行分开」的段子,哈哈:

我们利用前面的 string2Unicode() 方法,将其转化为 Unicode 编码,如下:

其实它们是由多个字符组合而成的,前面所看到的“空字符串”其实就是 U+200D(零宽连字)。

应用

零宽字符能做什么?

  • 传递信息:利用其不可见的特性,在未对零宽字符做过滤的网站插入不可见的隐形文本。
  • 水印:同样利用其不可见的特性,给我们的产品添加隐形水印。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

心的位置

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

yili302

文章 0 评论 0

晚霞

文章 0 评论 0

LLFFCC

文章 0 评论 0

陌路黄昏

文章 0 评论 0

xiaohuihui

文章 0 评论 0

你与昨日

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文