赞
踩
刚刚查询完ASCII码后,突然想搞清楚字符的编码。
计算机对数据的读取是按照一个字节的大小来读取识别的,那么面对全世界这么多语言,计算机怎么知道是多个字节表示一个符号,而不是分别表示多个符号呢?
结合了几篇前辈的文章,自己重新总结了下ASCII、Unicode、UTF-8的编码规则。
在计算机中所有信息都是以二进制位0
和1
来存储的。
8个比特位是一个字节(8 bit
= 1 Byte
),即一个字节一共可以用来表示256种不同的状态。
ASCII(American Standard Code for Information Interchange):美国信息交换标准代码是基于拉丁字母的一套电脑编码系统,主要用于显示电脑和其他电信设备中的现代英语和其他西欧语言。
电脑所有信息都是以二进制的方式来计算和存储的,那么人们能识别的文字和计算机中的二进制必然存在一种映射关系,ASCII就是最初的一种映射关系,也叫字符集。
比如在ASCII中,大写字母“A
”对应ASCII码的十进制是65
,二进制就是0100 0001
;小写字母a对应ASCII码的十进制是97
(二进制0110 0001
);阿拉伯数字0对应ASCII码的十进制是48
(二进制0011 0000
);还有空格32
(0010 0000
)等等一些键盘上所见的符号。
0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
---|---|---|---|---|---|---|---|
ASCII标准码统一规定为0 | 0/1 | 0/1 | 0/1 | 0/1 | 0/1 | 0/1 | 0/1 |
ASCII中的所有字符与对应编码,请看我的上一篇文章:《ASCII美国信息交换标准代码表》
随着计算机在全球的发展和普及开来,单字节只能够表示阿拉伯数字、英文字母、普通符号的标准ASCII码(128个),以及扩展部分欧洲语言字符之后的ASCII扩展码(256个),仍然无法完全表示比如中文、日文等其他语言字符。
所以各个国家便着手本国语言的编码,例如:中国国家标准总局1980年发行的GB2312以及之后的扩展版本GBK和GB18030。这导致了世界上存在着多种编码方式,出现了同一个二进制数字可以被解释成不同字符的现象。
GB2312因为只表示普通数字、字母、汉字,不表示其他语言,所以使用两个字节已经足够,但是它和汉字的映射,与之后的Unicode和汉字的映射没有任何关系。
比如汉字“一
”在Unicode中对应的字符集是“4E 00
”,而在GB2312中对应的字符集是“D2 BB
”。
要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。
所以,就需要一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。
直到之后出现了一个全球统一的编码规则叫Unicode,Unicode就像它的名字所表示的,这是一种包含所有符号的编码,它是为整合全世界的所有语言文字而诞生的。
任何文字在Unicode中都对应一个唯一的值,这个值称为代码点(code point)。
Unicode编码点分为17个平面(plane),每个平面包含2的16次(即65536)个码位(code point)。
17个平面的码位可表示为从U+xx0000到U+xxFFFF。
其中xx表示十六进制值从00到10(16进制),共计17个平面。
第一个00平面称为“基本多语言平面”(Basic Multilingual Plane,简称BMP)。
剩下16个平面(01-10)称为“辅助平面”(Supplementary Planes)。
Unicode标准规定其编码使用范围并不超过0x10FFFF。
Unicode字符集中汉字的范围是4E00 ~ 9FA5
。4E00是汉字“一”。
补充:鉴于 Unicode 原有的16位元空间不足以应用,从 Unicode 3.1 版本开始,设立了16个辅助平面,使 Unicode 的可使用空间由六万多字增至约一百万字。原有的 Unicode 空间称为基本平面或基本多文种平面 (Basic Multilingual Plane, 简称 BMP)。辅助平面字符要用上4字节来储存。
(1)如何才能区分Unicode和ASCII?
计算机对数据的读取是按照一个字节的大小来读取识别的,计算机怎么知道三个字节表示的是一个字符,而不是分别表示三个字符呢?
(2)一个字符用统一的字节数来表示,会造成存储空间的浪费。
我们已经知道,英文字母只用一个字节的ASCII码表示就够了,如果Unicode统一规定每个字符需要用相同的字节数来表示,每个字符用两个、三个,甚至四个字节表示,那么每个英文字母前都必然有一到三个字节全是二进制数0
,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。
注意:Unicode只是一个符号集,它只规定了各个字符所对应的二进制数,并没有规定在计算机中如何存储以及传输的问题。
对比 | UTF-8 | UTF-16 | UTF-32 | UCS-2 | UCS-4 |
---|---|---|---|---|---|
编码空间 | 0 ~ 10FFFF | 0 ~ 10FFFF | 0 ~ 10FFFF | 0 ~ FFFF | 0 ~ 7FFFFFFF |
最少编码字节数 | 1 | 2 | 4 | 2 | 4 |
最多编码字节数 | 4 | 4 | 4 | 2 | 4 |
是否依赖字节序 | 否 | 是 | 是 | 是 | 是 |
UTF-16可看成是UCS-2的父集,UTF-32可看成是UCS-4的子集。
通用多八位编码字符集(Universal Multiple-Octet Coded Character Set)也叫通用字符集(Universal Character Set, UCS),是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义的标准字符集。
通用多八位编码字符集包括了其他所有字符集。它保证了与其他字符集的双向兼容,即,如果你将任何文本字符串翻译到UCS格式,然后再翻译回原编码,你不会丢失任何信息。
UCS-2(Universal Character Set coded in 2 octets),是用定长2个字节来表示字符(定长编码),其取值范围为 U+0000~U+FFFF。
Unicode当前默认的版本是UCS-2,UCS-2 编码 与 Unicode码 完全一样,6w+的字符量已经足以用于全球的主要语言的大多数字符。
但是Unicode为了能表示更多的文字,还是提供了一个扩展机制(UCS-4,用四个字节表示代码点,最高位为0,取值范围为 U+00000000~U+7FFFFFFF),允许表示一百多万个字符。
比如,英文字母“A
”对应的Unicode(十六进制)是U+0041
,转换为十进制是65
,转换为二进制是0100 0001
,和ASCII码一致,只需要一个字节表示。
比如,中文“一
”对应的Unicode(十六进制)是U+4E00
,转换为十进制是19968
,转换为二进制是100 1110 0000 0000
,这个二进制有15位,需要至少2个字节表示。
具体的符号对应表,可以查询Unicode官网,也可以查询专门的汉字对应表,还有字符与Unicode编码在线转换。
随着互联网的普及,为了解决如何来表示Unicode、如何让计算机读懂Unicode的问题,强烈需要一种统一规定存储格式的编码方式,即UTF(Unicode Transformation Format),是Unicode的变长字符编码的方式。
Unicode的实现方式包括UTF-8、UTF-16、UTF-32,其中 UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。
编码类型 | 字节长度 | 说明 |
---|---|---|
UTF-8 | 1~4 | 由1~4个字节变长 |
UTF-16 | 2~4 | 2或4个字节变长,大部分是2字节,对于ASCII空间浪费 |
UTF-32 | 4 | 4个字节表示,空间极大浪费 |
UTF-8 最大的一个特点,就是它是一种可变长度的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8 的规则很简单,只有两条:
(1)对于单字节的符号,字节的最高位
二进制数设为0
,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和最初的前128个ASCII标准码是完全相同的。
第一位 | 第二位 | 第三位 | 第四位 | 第五位 | 第六位 | 第七位 | 第八位 |
最高位必须为0 | 字符的Unicode码 |
(2)对于n字节的符号(n > 1
),第一个字节的前n位
都设为1
,第n + 1位
设为0
,后面字节的前两位
一律设为10
。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
- 第一个字节:
- 高位前n位为`1`
- 第n+1位为`0`
- 其他字节:
- 高位以`10`开头
- 未提及的位使用对应的unicode补充,不足的在高位用0补充
Unicode 和 UTF-8 的映射关系如下表:
(UTF-8 编码方式中的x为字符的Unicode二进制编码)
Unicode符号范围 (十六进制) | UTF-8 编码方式 (二进制) | 说明 |
---|---|---|
00 00 00 00 ~ 00 00 00 7F | 0xxxxxxx | 完全兼容ASCII |
00 00 00 80 ~ 00 00 07 FF | 110xxxxx 10xxxxxx | 110表示需要用两个字节(当前字节和随后一个字节)来表示当前字符 |
00 00 08 00 ~ 00 00 FF FF | 1110xxxx 10xxxxxx 10xxxxxx | 1110表示需要用三个字节来表示当前字符 |
00 01 00 00 ~ 00 10 FF FF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | 同上,11110表示需要四个字节 |
总结,UTF-8 就是使用控制码+字符码
组成,控制码就是告诉计算机当前是单字节还是多字节,字符码就是对应的Unicode。
比如,汉字“一
”对应的Unicode(十六进制)是4E 00
,4E 00
在08 00 ~ FF FF
的范围内,所以对应的 UTF-8 需要三个字节。4E 00
对应的二进制为100 1110 0000 0000
,加上控制码后,对应的 UTF-8 为11100100 10111000 10000000
,控制码后面的高位不足时,用0
补充。
BOM(Byte Order Mark),字节顺序标记,出现在文本文件头部,Unicode编码标准中用于标识文件是采用哪种格式的编码,是UTF编码方案里用于标识编码的标准标记。
BOM是为 UTF-16 和 UTF-32 准备的,用户标记字节序(Byte Order)。拿UTF-16来举例,其是以两个字节为编码单元,在解释一个 UTF-16 文本前,首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是59 4E
,“乙”的Unicode编码是4E 59
。如果我们收到 UTF-16 字节流“59 4E
”,那么这是“奎”还是“乙”?(详情请看下一节Little endian 和 Big endian部分)
UTF-8是以字节为编码单元,没有字节序的问题。
UTF-8 文件中放置BOM主要是微软的习惯,但是放在别的系统上会出现问题。
不含BOM的UTF-8才是标准形式。
UTF-8 BOM是微软在自己的 UTF-8 格式的文本文件之前加上了“EF BB BF
”三个字节,Windows上的“Notepad(记事本)”等程序就是根据这三个字节来确定一个文本文件是 ASCII 的还是 UTF-8 的,然而这个只是微软暗自作的标记,其它平台上并没有对 UTF-8 文本文件做个这样的标记。
上一节已经提到,UCS-2 格式可以存储 Unicode 码(码点不超过0xFFFF
)。以汉字“周
”为例,Unicode 码是54 68
,需要用两个字节存储,一个字节是54
,另一个字节是68
。存储的时候,54
在前,68
在后,这就是Big endian方式;68
在前,54
在后,这是Little endian方式。
这两个古怪的名称来自英国作家斯威夫特的《格列佛游记》。在该书中,小人国里爆发了内战,战争起因是人们争论,吃鸡蛋时究竟是从大头(Big-endian)敲开还是从小头(Little-endian)敲开。为了这件事情,前后爆发了六次战争,一个皇帝送了命,另一个皇帝丢了王位。
- 第一个字节在前,就是“大头方式”(Big endian)。
- 第二个字节在前,就是“小头方式”(Little endian)。
那么,就会出现一个问题:计算机怎么知道某一个文件到底采用哪一种方式编码?
Unicode规范中推荐的标记字节顺序的方法是BOM。在UCS编码中,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做"零宽度非换行空格"(ZERO WIDTH NO-BREAK SPACE),用FE FF
表示。这正好是两个字节,而且FF
比FE
大1
。
注意:FE FF
在UCS中是不可见的字符,所以不应该出现在实际字节流传输中。
UCS规范建议我们在传输字节流前,先传输“ZERO WIDTH NO-BREAK SPACE”字符。
- 如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式。
- 如果一个文本文件的头两个字节是FF FE,就表示该文件采用小头方式。
微软Windows平台,有一个最简单的Unicode和 UTF-8 转化方法,就是使用内置的记事本程序notepad.exe
。打开文件后,点击文件
菜单中的另存为
命令,会跳出一个对话框,在最底部有一个编码
的下拉条。
“编码”里面有四个选项:ANSI
、UTF-16 LE
、UTF-16 BE
、UTF-8
、UTF-8 BOM
。
(1)ANSI
编码是不是一种特定的编码方式,具体是要取决于操作系统。
对于英文文件是ASCII
编码,对于日文文件是JIS
编码,对于简体中文文件是GB2312
编码(只针对Windows简体中文版,如果是繁体中文版会采用Big5
码)。
(2)UTF-16 LE
编码指的是UTF-16编码方式,采用Little endian格式(原来顺序)。
(3)UTF-16 BE
编码指的是UTF-16编码方式,采用Big endian格式(颠倒顺序)。
(4)UTF-8
编码指的是不带BOM的 UTF-8 编码。
(5)UTF-8 BOM
编码指的是带BOM的 UTF-8 编码。
从Windows10开始,记事本notepad.exe已经将带BOM和不带BOM的分开来了。
参考文献:
[1] Unicode官网
[2] 专门的汉字对应表
[3] 阮一峰的《字符编码笔记》
[4] 熊猫儿的《一文看懂ASCII,UNICODE,UTF8编码规则》
[5] ivy_0709的《详解windows记事本的4种编码方式》
[6] 我(零号萌新)的《ASCII美国信息交换标准代码表》
[7] 工具:字符与Unicode编码在线转换
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。