likes
comments
collection
share

一个小bug引发的思考(实体字符)

作者站长头像
站长
· 阅读数 20

俺在公司需求迭代的开发中遇到了这么一个问题,后端给俺返回了一个字符串:

一个小bug引发的思考(实体字符)

这串字符是一个超链接,本来期望在点击页面中某个按钮时,跳到这个地址中去。

但是问题来了!

莫名其妙的,也不知道什么原因导致的,后端返回的这个超链接字符串中带有":""/""/"这些看不懂是啥玩意儿的东西。

然后通过window.open()函数就跳转不了,必须把这些字符转化正常才行啊!

于是跑去特意查了一下,原来它们叫实体字符

什么是实体字符呢?

实体字符是一种HTML中的特殊字符编码方式,用于表示一些特殊字符,如小于号(<)、大于号(>)、和号(&)等,以避免这些字符被解释为HTML标签或命令。实体字符由一个实体名称或实体编号组成,实体名称以“&”开始,以“;”结束,实体编号以“&#”开始,以“;”结束。

乍一看,是蛮好理解的,但仔细思索片刻就感觉有些蒙了。

常见的字符编码一般有ASCII码、Unicode和UTF-8等,而在html文档中,唯一有效编码方式是"UTF-8"啊

为什么这么说?且先听俺普及一下有关字符编码的知识。

先来谈一谈,为什么需要字符编码呢?

  • 文档需要字符编码是因为计算机只能处理数字,而文本是由字符组成的,因此需要将字符转换为数字才能在计算机中进行处理和存储。
  • 字符编码是一种将字符转换为数字的规则或方法,它将每个字符映射到一个唯一的数字编码,以便计算机能够识别和处理文本。
  • 不同的字符编码有不同的映射规则和编码方式,正确的字符编码可以确保文本在不同的计算机系统和应用程序之间正确地显示和传输。

那么,有哪些常见的字符编码方式呢?

常见的字符编码包括ASCII码、Unicode和UTF-8。

  • ASCII码是一种最早的字符编码,它使用7位二进制数表示128个字符,包括英文字母、数字、标点符号和控制字符等。
  • Unicode是一种更加全面的字符编码,它使用16位二进制数表示65536个字符,包括世界上所有的语言文字和符号。
  • UTF-8是一种基于Unicode的可变长度字符编码,它使用1到4个字节表示不同的字符,可以节省存储空间。

于是俺得出了一个结论,只要不是01数字序列的文档,在计算机中传输就都需要指定字符编码,不然的话,计算机根本不认识它们啊!

而俺记得在html文档中,有一个元数据标签meta,它有个属性charset,专门用来声明文档的字符编码,其值必须是字符串 "utf-8",这是官方规定的,因为UTF-8 是 HTML5 文档的唯一有效编码

一个小bug引发的思考(实体字符)

于是,俺又进一步得出了一个结论,跟html有关的编码方式有两种,一种是"utf-8"编码,作用是把html文档内容转化成01数字序列,另一种则是实体字符编码方式,它是html中一种特殊的字符编码方式

所以,实体字符这种编码方式,应该只存在于html的文档中。

以下是俺罗列出的一些html中的实体字符(了解更多实体字符,点我):

显示结果描述实体名称实体编号
空格&nbsp;&#160;
<小于号&lt;&#60;
>大于号&gt;&#62;
&和号&amp;&#38;
"引号&quot;&#34;
'撇号&apos;&#39;
&cent;&#162;
£&pound;&#163;
¥&yen;&#165;
欧元&euro;&#8364;
§小节&sect;&#167;
©版权&copy;&#169;
®注册商标&reg;&#174;
商标&trade;&#8482;
×乘号&times;&#215;
÷除号&divide;&#247;

需要注意的是,实体字符有实体编号实体名称之分,但不管是使用实体编号还是实体名称,html都可以解析出来。

使用实体名称而不是实体编号的好处是,名称易于记忆,不过坏处是,浏览器也许并不支持所有实体名称(对实体编号的支持却很好)。

在一番探索之下,这个bug的谜团就变得明朗起来了。

虽然不知是何故,后端同学会返回带有实体字符的字符串,准确来说是实体字符编号,但是解决办法不过就是把实体字符转化为正常字符而已,在前端转化亦可!

转化实体字符有两种思路,一是通过html转,二是通过js转,原理也很简单:

  • 方式一是,既然实体字符是html特殊的编码方式,那么先通过html节点调用innerHTML的方式把字符串注入,再调用节点的innerText属性去取里面的字符串,这样一存一取,让dom节点自动完成了解析。
  • 方式二是,通过js把实体字符和显示字符做个对应关系,把字符串中的实体字符都替换成显示字符即可。

代码如下:

// html转义
function htmlDecode(text) {
  let temp = document.createElement("div")
  temp.innerHTML = text
  const output = temp.innerText || temp.textContent
  temp = null
  return output
}

// js转义(利用正则)
const ESCAPE_CHARACTERS = {
  'nbsp': ' ',
  'lt': '<',
  'gt': '>',
  'amp': '&',
  'apos': '\"',
  'ensp': '     ',
  'emsp': ' ',
  'quot': '"',
  'middot': '·',
  'brvbar': '¦',
  'mdash': '—',
  'ndash': '–',
  'ge': '≥',
  'le': '≤',
  'laquo': '«',
  'raquo': '»',
  'deg': '°',
  'bull': '•',
  'macr': '¯',
  '#64': '@',
  'ldquo': '“',
  'rdquo': '”',
  'rsquo': '‚',
  'lsquo': '‘',
}
// 处理转义字符
handleEscapeChar(str) {
  return str.replace(new RegExp(`&(${ Object.keys(ESCAPE_CHARACTERS).join('|') });`, 'g'), (all, t) => {
    return ESCAPE_CHARACTERS[t]
  })
}

最后,俺用第一种方式,成功的处理了这个没法跳转的小bug!

总结

得感谢后端同学,感谢这个小小的bug,让俺忍不住去查找一圈资料,最后也是学到了一些新东西。

实体字符,这个html知识领域中,一处不起眼的小角落,也是可以挖掘出有用的知识来的,哈哈!