关于字符编码,前端应该知道的

598 阅读14分钟

1. 基本概念

计算机中储存的信息都是用二进制数据表示的;而英文、汉字等字符是将二进制数据转换之后的结果。

将字符转化为二进制数据,称为"编码";反之,将二进制数解析成字符,称为"解码"。

字符集(Charset) :是一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。

字符编码(Character Encoding) :是一套规则或者说是映射关系,可以使用它对某套字符的集合,与其他的一个集合(如二进制数据)进行一一配对映射。即在符号集合与数字系统之间建立对应关系。

流程图.jpg

2. 常用字符集和字符编码

常见字符集:ASCII字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。

  • ASCII字符集:

ASCII字符集是美国人制定的,所以一共规定了128个字符的编码,主要包括控制字符(回车键、退格、换行键等)、可显示字符(英文大小写字符、阿拉伯数字和西文符号)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的一位统一规定为0

  • ASCII扩展字符集(EASCII):

为了表示更多的欧洲常用字符,对ASCII进行了扩展,利用字节中闲置的最高位编入新的符号。ASCII扩展字符集使用8位(bits)表示一个字符,共256字符。

  • GBXXXX字符集:

要处理中文显然一个字节是不够的,至少需要两个字节。中国制定了GB2312(GB2312-80)编码。

  • GB2312:GB2312通常采用EUC储存方法,以便兼容于ASCII。每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”(也称“区字节)”,第二个字节称为“低位字节”(也称“位字节”)。区字节使用0xB0-0xF7,位字节使用0xA1-0xFE。
  • 全角/半角:在这些编码里,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符。
  • GBK:由于GB 2312-80只收录6763个汉字,有不少汉字并未收录。微软利用GB 2312-80未使用的编码空间,收录GB 13000.1-93全部字符制定了GBK编码。原始GB13000一直未被业界采用,后续国家标准GB18030技术上兼容GBK而非GB13000。
  • GB18030:是中华人民共和国现时最新的内码字集。与GB2312-1980完全兼容,与GBK基本兼容,支持GB13000及Unicode的全部统一汉字,共收录汉字70244个。与UTF-8相同,采用多字节编码,每个字可以由1个、2个或4个字节组成。
  • BIG5字符集:

Big5,又称为大五码或五大码,是使用繁体中文中最常用的电脑汉字字符集标准,以两个字节来表示。

  • Unicode字符集:

世界各国有各种语言和字符,设计和实现了各种编码方案和字符集,各国有各国的标准,就会不可避免地出现冲突。

为了解决这个问题,一个伟大的创想产生了——Unicode。Unicode把所有语言都统一到一套字符集中。

Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

  • 码点

码点是 Unicode 用于唯一表示某个字符的标识,码点的表示的形式为 U+[xx]xxxx,x 代表一个十六制数字,一般可以有 4-6 位,不足 4 位补 0 。

  • 字符平面:

目前的Unicode字符分为17组编排,每组称为平面(Plane),而每平面拥有65536(即2^16)个代码点。

平面始末码点中文名称详情
0号平面BMPU+0000 - U+FFFF基本多文种平面各种基本语言文字、字母、符号(包括中日韩统一表意文字扩展A区、私人使用区PUA
1号平面SMPU+10000 - U+1FFFF多文种补充平面(第一辅助平面)主要摆放绝大多数古代文字(西方),现时已不再使用或很少使用文字、速记、数学字母符号、音符、图形符号及用于学者的专业论文中使用的古老或过时的语言书写符号,以及网络通信等使用的表情符号(emoji)
2号平面SIPU+20000 - U+2FFFF表意文字补充平面(第二辅助平面)罕用的中日韩的汉字或地区的方言用字(中日韩统一表意文字扩展B-F区,中日韩兼容表意文字增补)
3号平面TIPU+30000 - U+3FFFF表意文字第三平面(第三辅助平面)用来摆放汉字扩展区G,并规划用于摆放甲骨文、金文、小篆、中国战国时期文字等
4号平面至13号平面U+40000 - U+DFFFF(第四-十三辅助平面)
14号平面SSPU+E0000 - U+EFFFF特别用途补充平面(第十四辅助平面)目前仅用于摆放“语言编码标签”和“字形变换选取器”,它们都是控制字符。
15号平面PUA-AU+F0000 - U+FFFFF保留作为私人使用区(A区)(第十五辅助平面)
16号平面PUA-BU+100000 - U+10FFFF保留作为私人使用区(B区)(第十六辅助平面)
  • USC字符集:

与Unicode相似,在1991年前后开始合并。USC主要的字符编码方式有:USC-2、USC-4

  • UTF-32与UTF-16编码:

UTF-32使用4字节来表达每个Unicode字符。

UTF-16将0–65535范围内的Unicode字符编码成2个字节,如果真的需要表达那些很少使用的超过这65535范围的Unicode字符,则需要使用一些诡异的技巧来实现。

在常见的字符码位中,UCS-2和UTF-16可以认为是相同的,UTF-16是UCS-2的超集。

  • UTF-8编码:

UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,也是一种前缀码。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容。

它使用1~4(1~6)个字节表示一个符号,根据不同的符号而变化字节长度。

UTF-8以字节为编码单元,它的字节顺序在所有系统中都是一様的,没有字节序的问题。

UTF-8编码规则:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。

2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。

UTF-8 二进制Unicode 码点字符
UTF8-10xxxxxxxU+0000 -U+007FUS-ASCII字符
UTF8-2110xxxxx 10xxxxxxU+0080 -U+07FF带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母
UTF8-31110xxxx 10xxxxxx 10xxxxxxU+0800 -U+FFFF其他基本多文种平面(BMP)中的字符(这包含了大部分常用字,如大部分的汉字)
UTF8-411110xxx 10xxxxxx 10xxxxxx 10xxxxxxU+10000 -U+1FFFFF辅助平面(实际上 Unicode 只规定到U+10FFFF,所以4个字节足够了)
UTF8-5111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxxU+200000 -U+3FFFFFF
UTF8-61111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxxU+4000000 -U+7FFFFFFF

3. HTTP/HTML/CSS

  • UTF-8:

UTF-8一直是互联网的最主要的编码形式,互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。

  • Accept-Charset:浏览器申明自己接收的字符集,这就是本文前面介绍的各种字符集和字符编码,如gb2312,utf-8(通常我们说Charset包括了相应的字符编码方案);

  • Content-Type:WEB服务器告诉浏览器自己响应的对象的类型和字符集。例如:Content-Type: text/html; charset='gb2312'

  • HTML 字符实体:

字符实体引用(character entity reference),是 HTML/SGML 中对字符的一种转义序列表示, 通过文件类型描述(DTD)中预定义且明确声明的SGML命名实体的引用来描述Unicode字符。

目的是对当前文档的编码方式不能包含的字符,提供一种转义表示。也用于 icon-font 等场景。

一个实体声明通过<!ENTITY name "value">DTD或XML语法来创建。实体声明中定义的名字可以在随后的XML中使用。

字符实体引用的格式是:& name ;name 是实体的名字。

name可以是名称 (大小写敏感),也可以是编号。编号即为 Unicode 的10进制或者16进制的码点。

比如小于号可以用 &lt; &#60; &#060; &#x3c; &#x3C; &#x03c;表示。

部分 HTML 预留字符实体:

显示结果描述实体名称实体编号
不间断空格(Non-breaking Space)&nbsp;&#160;
<小于号&lt;&#60;
大于号&gt;&#62;
&和号&amp;&#38;
"引号&quot;&#34;
'撇号&apos; (IE不支持)&#39;
分(cent)&cent;&#162;
£镑(pound)&pound;&#163;
¥元(yen)&yen;&#165;
欧元(euro)&euro;&#8364;
§小节&sect;&#167;
©版权(copyright)&copy;&#169;
®注册商标&reg;&#174;
商标&trade;&#8482;
×乘号&times;&#215;
÷除号&divide;&#247;
  • CSS字符转义

在 CSS 中使用字符串时,因为默认编码是utf-8,可以用utf-8的文本,或者使用反斜杠 \ 进行转义表示。

CSS中分为标识符和字符串,都支持字符的转义,即使用反斜杠 \ 进行转义表示。对于开头数字的标识符,开头处数字必须转义;特殊字符可以使用转义消除特殊含义。

详见 [译] 最详细的 CSS 字符转义处理

4. JavaScript

  • JavaScript 文件编码

    • ES6 之前

JavaScript 语言采用 Unicode 字符集,编码方式是UCS-2。因为在 JavaScript 语言出现的时候,没有其他选择,只有 UCS-2 可用。

  • ES6

ECMAScript 6 大幅增强了 Unicode 支持,使用了UTF-16编码。

  • 正确识别 4 字节的码点,修复length和reverse
  • JavaScript 允许直接用码点表示 Unicode 字符,写法是 "反斜杠 + U + 码点"(注意:里面不需要添加 + 号)。对 4 字节的码点,写法是 "反斜杠 + U + { + 码点 + }"
  • 提供了 u 修饰符,对正则表达式添加 4 字节码点的支持
  • 新增codePointAt方法
  • 新增了fromCodePoint方法
  • File

在JavaScript中,File对象代表一个文件,用来读写文件信息,继承于Blob。

declare var File: {

    prototype: File;

    new(fileBits: BlobPart[], fileName: string, options?: FilePropertyBag): File;

};



declare var Blob: {

    prototype: Blob;

    new(blobParts?: BlobPart[], options?: BlobPropertyBag): Blob;

};



interface FilePropertyBag extends BlobPropertyBag {

    lastModified?: number;

}



interface BlobPropertyBag {

    endings?: EndingType;

    type?: string;

}

在创建新的File和Blob对象实例时,可以在options中传入type,用于表示将要放到文件中的内容的 MIME 类型,类似于http协议中的Content-Type,例如 text/plain;charset=UTF-8。

  • encodeURI /encodeURIComponent

    • 作用:将文本字符串编码为一个有效的统一资源标识符 (URI)。

      • 一般来说,URI 只能使用英文字母、阿拉伯数字和某些标点符号,不能使用其他文字和符号。
      • "; / ? : @ & = + $ , #" 等保留符号在 URI 中有特殊含义,所以在表示这些符号本身时需要转义。
    • 区别:

用途不编码的字符
encodeURI对整个URL进行编码ASCII字母 数字 ~!*()'@#$&=:/,;?+
encodeURIComponent对URL的组成部分(如参数)进行编码ASCII字母 数字 ~!*()'
  • 编码规则:

    • 保留字符:把该字符的ASCII的值表示为两个16进制的数字,然后在其前面放置转义字符百分号("%")
    • 非ASCII字符: 需要转换为UTF-8字节序列, 然后每个字节按照保留字符的方式表示
    • 对百分号字符%:"%25"

5. 文件

  • BOM

一些操作系统比如Windows使用BOM来标记文本文件的编码方式。

  • 对于 16 位或者 32 位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节排放的问题。大小端是数据在存储器中的存放顺序,大端模式,是指数据的高字节在前,保存在内存的低地址中,与人类的读写法一致,数据的低字节在后,保存在内存的高地址中,小端与之相反。不同操作系统读取多字节的顺序不一样,x86和大部分的OS(如windows,FreeBSD,Linux)使用的是小端模式;但有些OS比如Mac OS是大端模式。
  • BOM(Byte Order Mark, 字节序标志),在UCS 编码中有一个叫做 "Zero Width No-Break Space"的字符,编码是 FEFF。当使用UTF-16或UTF-32编码时,不同的计算机系统会以不同的顺序保存字节,所以将BOM包含在文档的开头来表示所使用的字节顺序。
  • UTF-8 不需要 BOM 来表明字节顺序,但可以用 BOM 来表明编码方式。字符 "Zero Width No-Break Space" 的 UTF-8 编码是 EF BB BF。
字符编码表示(十六进制)
UTF-8EF BB BF
UTF-16(大端序)FE FF
UTF-16(小端序)FF FE
UTF-32(大端序)00 00 FE FF
UTF-32(小端序)FF FE 00 00
UTF-72B 2F 76和以下的一个字节:`[ 38392B2F ]`
UTF-1F7 64 4C
UTF-EBCDICDD 73 66 73
Unicode标准压缩方案0E FE FF
BOCU-1FB EE 28 及可能跟随着FF
GB-1803084 31 95 33
  • 判断编码

    • 通过文件的前三个字节来判断:有些编码格式会存在文件的前面3个字节中(BOM)。但是这种方式的局限性比较大,推测出来的准确率也比较低。

    • 通过特殊字符来判断:通过某些编码格式编码的文件中会出现一些特殊的字节值,因此可以通过判断文件中是否有这些特殊值来推测文件编码格式。此方准确率也不高,不推荐使用。

    • 通过第三方工具库来判断:

      • cpdector 是一款开源的文档编码检测工具,可以检测 xml,html文档编码类型。是基于统计学原理来推测文件编码的,但是也不保证推测结果的准确性。

      • ICU 库的推测逻辑基于IBM过去几十年收集的字符集数据,理论上也是基于统计学的。这种方式统计的结果准确性也较高。

  • Base64

Base64是一种常见的二进制编码方法,基于64个可打印字符来表示任意二进制数据。可打印字符包括 A-Z、a-z、0-9、+、/。

因为 2^6 = 64 ,所以每个Base64可打印字符可表示6个比特的数据。因此,3个字节相当于24个比特,对应于4个Base64可打印字符。

如果要编码的字节数不能被3整除,多出1个或2个字节,则需要使用下面的方法进行处理:先使用0字节值在末尾补足,使其能够被3整除,然后再进行Base64的编码。在编码后的Base64文本后加上一个或两个=号,代表补足的字节数。

在一些情况下存在变种的Base64,将可打印字符中的 +、/ 符号进行变更。

参考链接