由于计算机内部只能识别和处理二进制代码,所以字符都必须按照一定的规则用一组二进制编码来表示。
在学习编码之前,需要先了解一下 字符集与编码的关系:
字符集(Character Set)是字符的集合,定义系统能处理哪些字符;编码(Encoding)则规定这些字符在计算机内部的表示方式。
字符编码ASCII码(编码字符集)
目前,国际上普遍采用的一种字符系统是7位二进制编码的ASCII码,它可表示10个十进制数码、52 个英文大写字母和小写字母(A~Z, a~z)及一定数量的专用符号(如$、%、+、=等),共128个字符。为了存入计算机,通常最高位补0,凑足1B。
在ASCII码中,编码值0~31为控制字符,用于通信控制或设备的功能控制;编码值32~126共95个字符称为可印刷字符;编码值127是DEL码。
0~9的ASCII码值为48(0110000)~57(0111001),即去掉高3位,只保留低4位,正好是二进制形式的0-9。
由于ASCII码的局限性,各国的语言不能完整地表示出来。于是对 ASCII 字符集做了拓展。
汉字的表示和编码
目前采用 GB 2312-80标准 : 汉字+各种符号共7445个。用两个字节表示一个汉字,每字节用七位码。(1个汉字相当于两个英文字符)
规定:ASCII 值小于 127 的字符的意义与原来 ASCII 集中的字符相同,但当两个 ASCII 值大于 127 的字符连在一起时,就表示一个简体中文的汉字。
为了在解码时操作的统一,在 ASCII 里本来就有的数字、标点、字母都统一重新表示为了两个字节长的编码,这就是常说的 “全角” 字符,而原来在 127 号以下的就叫 “半角” 字符。
汉字编码包括:
输入编码
- 区位码
- 国标码
- 汉字内码
- 汉字字形码
区位码:94个区,每区94个位置。是4位十进制数,前2位是区码,后2位是位码。
国标码:将十进制的区位码转换成十六进制数后,再在每字节上加上20H。国标码两字节的最高位都是0。
汉字内码:为了方便计算机区分中文字符和英文字符,将国标码两字节最高位都改为“1“,这就是汉字内码。
GBK
GBK 是对 GB2312 的一个扩展,兼容 GB2312,因此也兼容 ASCII,也是一个变长编码方案。下面是一个简介:
GBK 总体编码范围为 8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间,总计 23940 个码位,共收入 21886 个汉字和图形符号,其中汉字(包括部首和构件)21003 个,图形符号 883 个。
GBK 是国家有关部门与一些信息行业企业等一起合作推出的方案,但并未作为国家标准发布,只是一个事实上的标准,一个过渡方案,为 GB18030 标准作的一个准备。
世界上各国都有不同的编码方式,0—127 表示的符号依然都是一样的,因为他们都兼容 ASCII 码,但127之后的同一个二进制数字可以被解码成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。
Unicode
把所有语言都统一到一套编码里,且兼容 ASCII。
ASCII、GBK 等类编码模式的字符集和编码方式都是一一对应的,Unicode是字符集,UTF-32/ UTF-16/ UTF-8是三种字符编码方案。
整个编码可分为两个过程。首先,将程序中的字符根据字符集中的编号数字化为某个特定的数值,然后根据编号以特定的方式存储到计算机中。
编号就相当于 ASCII 码中的 ASCII 值,它就是 Unicode 字符集中唯一表示某个字符的标识,在 Unicode 也称作码点(Code Point)。
码点转换成各种编码,又涉及到编码过程中定长与变长两种实现方式,UTF-32 就属于定长编码,即永远用 4 字节存储码点,而 UTF-8、UTF-16 就属于变长存储,UTF-8 根据不同的情况使用 1-4 字节,而 UTF-16 使用 2 或 4 字节来存储码点。
UTF-32
在 UTF-32 这种定长的编码方式下就表示每 4 个子节一个断句,那么字符 A 的码点 U+0041(二进制为 1000001)被 UTF-32 编码后就会变成如下形式存储在计算机中:
00000000 00000000 00000000 01000001
它会将 4 个字节中空出的高位全部填充为 0。这种表示的最大缺点是占用空间太大,因为不管都大的码点都需要四个字节来存储,非常的占空间,那么如何突破这个瓶颈呢?变长方案应运而生。
UTF-8
UTF-8 属于变长的编码方式,它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。使用的是高位保留的方式来区别不同变长,具体方式如下:
- 对于单字节的符号,字节的第一位设为
0
,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。 - 对于
n
字节的符号(n > 1
),第一个字节的前n
位都设为1
,第n + 1
位设为0
,后面字节的前两位一律设为10
。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制) |
---|---|
0000 0000-0000 007F | 0xxxxxxx |
0000 0080-0000 07FF | 110xxxxx 10xxxxxx |
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
解码 UTF-8 编码也很简单了,如果一个字节的第一位是 0,则这个字节单独就是一个字符;如果第一位是1
,则连续有多少个 1,就表示当前字符占用多少个字节,”丑” 有三个 1 表示占三个字符,然后取出有效位即可。
Emoji
Unicode 为 Emoji 分配码点。 Emoji 符号就是一个文字,它会被渲染为图形。
以上三种都是属于文字的编码,但在计算机中,还有类似图片这种媒体资源。因为图片的二进制编码中包含有很多不可见字符,无法用文本展示。
Base64
base64编码是用来解决把不可打印的内容塞进可打印内容的需求的。比如把图片存到数据库,图片数据归根到底还是一堆二进制串,用base64编码后的显示成的字符串就大大缩小的长度,可以存到数据库。
所谓Base64,就是:小写字母a-z、大写字母A-Z、数字0-9、符号"+"、"/"(再加上作为垫字的"=",实际上是65个字符)----作为一个基本字符集。然后,其他所有符号都转换成这个字符集中的字符。
具体来说,转换方式可以分为四步。
第一步,将每三个字节作为一组,一共是24个二进制位。
第二步,将这24个二进制位分为四组,每个组有6个二进制位。
第三步,在每组前面加两个00,扩展成32个二进制位,即四个字节。
第四步,根据下表,得到扩展后的每个字节的对应符号,这就是Base64的编码值。
因为,Base64将三个字节转化成四个字节,因此Base64编码后的文本,会比原文本大出三分之一左右。
如果字节数不足三,则这样处理:
1.二个字节的情况:将这二个字节的一共16个二进制位,按照上面的规则,转成三组,最后一组除了前面加两个0以外,后面也要加两个0。这样得到一个三位的Base64编码,再在末尾补上一个"="号。
比如,"Ma"这个字符串是两个字节,可以转化成三组00010011、00010110、00010000以后,对应Base64值分别为T、W、E,再补上一个"="号,因此"Ma"的Base64编码就是TWE=。
2.一个字节的情况:将这一个字节的8个二进制位,按照上面的规则转成二组,最后一组除了前面加二个0以外,后面再加4个0。这样得到一个二位的Base64编码,再在末尾补上两个"="号。
比如,"M"这个字母是一个字节,可以转化为二组00010011、00010000,对应的Base64值分别为T、Q,再补上二个"="号,因此"M"的Base64编码就是TQ==。
计算机中通用的字符编码的工作方式
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器。
Web 中的编码
要正确显示HTML页面,web浏览器必须知道页面中使用的字符集。
<!-- html -->
<meta charset="UTF-8">
<!-- xml -->
<?xml version="1.0" encoding="UTF-8"?>
比较HTML和xml中的写法:xml的写法更加规范
- charset 是 character set 的简写,即字符集。
- encoding 是 charset encoding 的简写,即字符集编码,简称编码。
指定了编码,它所对应的字符集自然就指定了,编码才是我们最终要关心的。
如何确定一个网页中编码
- 文档内的编码声明
- 响应体中的content-type字段中的编码信息
- BOM
BOM=Byte Order Mark,“字节顺序标识”。响应流中的开头字节。
如果有 BOM,就能直接知道对应的编码
- 缺省
缺省方式是以上几种方式都失效时的兜底方案,没有 BOM,没有 header,没有 meta,浏览器只好“蒙”一个编码。
如果多方式并存,且给出的编码信息不一致,通常按这样的优先级来取舍:
BOM > 响应头编码 > 文档内编码声明
HTML 编码
在 HTML 中,某些字符是预留的,比如不能使用小于号(<
)和大于号(>
),这是因为浏览器会误认为它们是标签。如果希望正确地显示预留字符,我们必须在 HTML 源代码中使用字符实体(character entities)。
字符实体: // <
& entity_name; // <
或
& entity_number; // <
URI 编码
编码原因:
考虑到 URI 在各种平台间传输时的兼容性,URI 规范中规定只有 US-ASCII 字符集中的字符可以直接出现在 URI 中。事实上,甚至 ASCII 本身的许多字符也不允许直接出现在 URI 中,有的也要转义。URL 路径中的中文需要转义,具体编码用的是 utf-8。
可以直接使用的: 26 个大小写字母,10 个数字,四个标点符号:-
,.
,_
,~
(“短横”、“点号”、“下划线”、“波浪线”)。其它的有的属于保留字,用作为分隔符(delimiters),它们有:
":" / "/" / "?" / "#" / "[" / "]" / "@" / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
比如正斜杠“/”就是一个最常用的分隔符。如果想在 URL 中包含这样符号,又不想它们被解析为分隔符,就要对其转义。其它的还有一些不可以打印的字符也要转义,比如“空格”等。
保留字符在不做分隔符或者具有特殊含义的时候是需要编码的。如果使用了一些其他文字和特殊字符,则需要通过编码的方式来进行表示:
%编码
由于 URI 在协议中只挑选了部分ASCII 字符,数字以及符号,那么当需要表示不在这个范围之内的符号,字符,或者该字符在 URI 中被用来分隔符等特殊用途时,就需要对这个字符进行%编码。百分号编码也可以叫做URLEncode,其中的每一个部分用【%XX】来表示,其中 XX 表示一个十六进制的数。
encodeURI 会将需要编码的字符转换为 UTF-8
的格式。对于非转义字符以及保留字符(;,/?:@&=+$#
)不会进行转义。
// URL中包含中文
encodeURI('http://www.帅.com'); // http://www.%E5%B8%85.com
// 值的内容为特殊符号。值为?&
encodeURI('http://a.com?key=?&'); // "http://a.com?key=?&"
encodeURIComponent 会编码所有的 URL 保留字:
encodeURIComponent('https://aotu.io/')
// "https%3A%2F%2Faotu.io%2F"
encodeURI('https://aotu.io/')
// "https://aotu.io/"
在处理页面跳转、跟服务器端进行交互时, 凡涉及到对URI进行解析的、最好用encodeURIComponent进行编码避免部分数据的丢失.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。