从 meta 标签开始说起

远子 •  2021年02月28日

前端代码离不开 html,html 有很多标签,在 html 的 head 标签里可以:

  1. 通过 title 标签声明文档标题;
  2. 通过 link 标签引入 CSS 文件或者设置 favicon;
  3. 通过 script 标签引入 JS 文件;
  4. 通过 meta 标签设置元数据,meta 标签有以下几种用途:

    1. 通过 meta 标签的 charset 属性设置字符编码,例如:<meta charset="utf-8">
    2. 通过 meta 标签的 name 属性设置文档级别的元数据:

      1. 添加作者:<meta name="author" content="rmlzy">
      2. 添加描述:<meta name="description" content="描述">
      3. 添加关键字:<meta name="keyword" content="关键字">
      4. 其他可以参考 MDN...
    3. 通过 meta 标签的 http-equiv 属性,可以参考 MDN

      1. <meta http-equiv="content-security-policy" content="">:设置内容安全策略
      2. <meta http-equiv="content-type" content="text/html; charset=utf-8">:指定文档编码;
      3. <meta http-equiv="x-ua-compatible" content="IE=edge">:IE专用。
      4. <meta http-equiv="refresh" content="10">:每隔 10 秒刷新一次文档;
      5. <meta http-equiv="default-style" content="">:设置默认 CSS 样式表组的名称。
    4. 通过 meta 标签的 itemprop 属性定义用户的元数据。

这篇文章主要分析一下 meta 标签的 charset 属性。

meta 标签的 charset 属性

<meta charset=""> 标签声明了文档的字符编码,其值不区分大小写。

如需查看所有可用的字符编码,可以访问IANA字符集

charset 常用的值有:

  • UTF-8
  • GB2312
  • GBK
  • ...

如果 charset 的值定义错误,浏览器将无法正确展示文档,举个例子,在浏览器中预览以下 html:

<html>
  <head>
    <meta charset="gb2312">
  </head>
  <body>
    這是一段繁體中文。
  </body>
</html>

效果如下:

image-20210303114726673

乱码的原因是 gb2312 字符集不包括繁体中文。


另外除了声明 html 文档的字符规范,charset 在很多地方都有用:

  • 在 CSS 中,用于声明样式表使用的字符编码:

    @charset "UTF-8";
  • 在 HTTP header 的 Accept-Charset 请求头中,用于声明客户端可以处理的编码类型:

    Accept-Charset: utf-8;q=0.5,iso-8859-1;q=0.1
    # 其中 q 的值代表优先顺序,上边的意思是优先使用 utf-8。
  • 在 HTTP Header 的 Content-Type 请求头中,用于声明响应内容的编码类型:

    Content-Type: text/html; charset=utf-8

常见的字符编码

我们知道计算机只能存储和运算二进制数,无法直接处理字母、数字、标点符号、中文、韩文、日文等等,需要将它们映射成二进制数。

简单来说,字符编码表存储了现代字符和二进制的映射关系。

根据使用场景不同,字符编码有很多种类,比如英文用 ASCII,简体中文有 GB2312,繁体中文用 GBK,日文用 ISO 2022等等。

ASCII

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统。

ASCII 码只能显示 26 个英文字母、阿拉伯数字和英文标点符号,ASCII 码目前有 128 个,可以在这里查看。

我们可以把字符编码理解为一个 JS 中的 Map,ASCII 码的 JS 表示大概是这样的:

const ASCII = {
  // 控制字符
  0: '',
  // ...
  
  // ---- 数字 ----
  32: " ", // 空格
  48: "0",
  49: "1",
  50: "2",
  51: "3",
  // ...
  57: "9",
  
  // ---- 英文字母(大写) ----
  65: "A",
  66: "B",
  67: "C",
  68: "D",
  // ...
  90: "Z",
  // ---- 英文字母(小写) ----
  97: "a",
  98: "b",
  99: "c",
  100: "d",
  // ... 
  122: "z",
  
  // ---- 标签符号 ----
  123: "{",
  124: "|",
  125: "}",
  126: "~",
  127: "" // 删除
};

其中 0 - 127 这 128 个十进制的数字,也叫做字符的 Code。

ASCII 码分为两种:

  • 0 - 31 (前32个)是不可打印的控制字符,用来控制设备(比如打印机);
  • 32 - 127(后 156 个)是打印字符,包括字母、数字、标点符号等。

举个例子,当计算机存储字符串 Hello World! 时,根据上边的 ASCII 码表可以得到:

H        -> 十进制为 72        -> 二进制为 01001000
e        -> 十进制为 101        -> 二进制为 01100101
l        -> 十进制为 108        -> 二进制为 01101100
l        -> 十进制为 108        -> 二进制为 01101100
o        -> 十进制为 111        -> 二进制为 01101111
         -> 十进制为 32        -> 二进制为 00100000
W        -> 十进制为 87        -> 二进制为 01010111
o        -> 十进制为 111        -> 二进制为 01101111
r        -> 十进制为 114        -> 二进制为 01110010
l        -> 十进制为 108        -> 二进制为 01101100
d        -> 十进制为 100        -> 二进制为 01100100
!        -> 十进制为 33        -> 二进制为 00100001

因此 Hello World 的十进制表示为:

072101108108111328711111410810033

二进制表示为:

00000000010010000110010101101100011011000110111100100000010101110110111101110010011011000110010000100001

有了对应的二进制数,计算机便可以存储、操作 Hello World! 这个字符串了。

在英语中,128 个符号便可以表示所有的字符,但是用来表示其他语言是不够的。

比如,在法语中,字母上方有注音符号,它就无法用 ASCII 码表示,于是就有了 ASCII 扩展表,ASCII 扩展表可以表示 256 个字符。

对于中文来说,256 个远远不够。简体中文常见编码为 GB2312,用两个字节表示一个汉字,理论上可以表示 $256 * 256 = 65536$ 个字符。

GB2312

GB2312 是中国简体中文字符集,全称《信息交换用汉字编码字符集》,又称 GB0,GB 是“国标”的意思。

可以这样理解:GB2312 = ASCII + 简体中文,对于英文字母、空格、阿拉伯数字等 Code 在 127 以内的字符,使用 ASCII 码。可以参考GB2312编码表。GB2312 共收录了 6763 个汉字,同时收录了拉丁字母、希腊字母、日文平假名等 682 个字符。

GB2312 对汉字做了“分区”处理,共计 94 个区,每个区 94 个汉字,理论上可以表示 $94 * 94 = 8836$ 个字符。

GB2312 的分区:

  • 01 - 09 区为特殊符号,如下图:image-20210303104147644
  • 10 - 15 区目前为空;
  • 16 - 55 区为一级汉字,按拼音排序,如下图:image-20210303104340606
  • 56 - 87 区为二级汉字,按部首/笔画排序:image-20210303104510414
  • 88 - 94 区目前为空。

举个例子,当计算机存储字符串 你好 世界! 时,根据上边的 GB2312 码表可以得到(注意空格使用 ASCII 码):

你        -> 十六进制为 C4E3        -> 二进制为 1100010011100011
好        -> 十六进制为 BAC3        -> 二进制为 1011101011000011
         -> 十进制为 32        -> 十六进制为 20        -> 二进制为 00100000
世        -> 十六进制为 CAC0        -> 二进制为 1100101011000000
界        -> 十六进制为 BDE7        -> 二进制为 1011110111100111
!        -> 十六进制为 A3A1        -> 二进制为 1010001110100001

因此 你好 世界! 的十六进制表示为:

C4E3BAC320CAC0BDE7A3A1

二进制表示为:

1100010011100011101110101100001100100000110010101100000010111101111001111010001110100001

至此,计算机便可以存储、操作 你好 世界! 这个字符串了。

GB2312 对于简体中文来说足够了,但是无法表示罕用字和繁体字,就有了 GBK 编码。

GBK

GBK 是在 GB2312 基础上的内码扩展规范,GBK 共收录 21003 个汉字和 883 个图形符号,GBK 是“国标扩展”的意思。

GBK 向下兼容 GB2312,可以这样理解:GBK = GB2312 + 繁体中文,即:GBK = ASCII + 简体中文 + 繁体中文。可以参考GBK编码表。GBK 最多可以表示 23940 个字符。

GB18030

GB 18030 全称《信息技术中文编码字符集》,其对 GB2312 完全兼容,与 GBK基本向后兼容,并支持 Unicode 的所有码位。GB 18030 共收录汉字 70244 个。

GB18030 有以下特点:

  • 采用边长多字节编码,每个字可以有1个、2个或4个字节组成;
  • GB18030 最多可定义 161 万个字符;
  • 完全支持 Unicode,支持少数民族文字、日文、韩文、emoji字符等等。

一张图看懂 ASCII、GB2312、GBK、GB18030 的关系:

image-20210303131800213

Unicode

Unicode 又称万国码,是计算机科学领域的业界标准。它整理、编码了世界上大部分的文字系统,使得电脑可以用更为简单的方式来呈现和处理文字。

Unicode 至今仍在不断增修,每个新版本都加入更多新的字符。目前最新的版本为2020年3月公布的13.0.0,已经收录超过13万个字符。

可以这样理解:Unicode = ASCII + 简体中文 + 繁体中文 + 日文 + 韩文 + 俄文 + ...,是全球通用的编码标准。

字符串 简体中文 繁體中文 english 的 Unicode 格式为:

\u7b80\u4f53\u4e2d\u6587 \u7e41\u9ad4\u4e2d\u6587 english

UTF-8

UTF-8(8-bit Unicode Transformation Format)是一种针对 Unicode 的可变长度字符编码,他可以用一至四个字节对 Unicode 字符集中的所有有效码点进行编码,属于 Unicode 标准的一部分。

自 2009 年以来,UTF-8 一直是万维网的最主要编码形式。

UTF-8 的优点:

  • ASCII 是 UTF-8 的一个子集;
  • 使用标准的面向字节的排序例程对 UTF-8 排序将产生与基于 Unicode 代码点排序相同的结果;
  • UTF-8和UTF-16都是可扩展标记语言文档的标准编码;
  • 任何面向字节字符串搜索算法都可以用于UTF-8的数据(只要输入仅由完整的UTF-8字符组成);
  • UTF-8字符串可以由一个简单的算法可靠地识别出来;

UTF-8 的缺点:

  • 编写不良的解析器
  • 不利于正则表达式检索

Unicode 和 UTF-8 的关系:

Unicode 只是一组字符设定,或者说从数字和字符之间的逻辑映射的概念编码,但是它并没有指定代码点如何在计算机上存储。UTF-8、UTF-16 等都是 Unicode 的实现。

简单来说,Unicode 只是一个标准,UTF-8 是该标准的 8 bits 实现。

MySQL 字符编码集中有两套 UTF-8 编码实现:utf8 和 utf8mb4。其中 utf8 是一个字最多占 3 字节空间的编码实现;而 utf8mb4 则是一个字最多占 4 字节空间的编码实现,也就是 UTF-8 的完整实现。

在 MySQL 中如果需要支持 emoji 表情,把编码设置为 utf8mb4 即可。

(完)