字符编码的故事
不管是以前用 C#。 还是现在用 javascript.
一直对于字符编码这回事儿,一直不清楚.
在读 阮一峰写的ES6入门的时候,讲到了字符编码. 于是趁着学习这个的机会了解一下。
- ascii
- unicode
- utf-8
- utf-16
我们都知道 一个字节(Byte) = 8位(bit)
所谓字节,原意就是用来表示一个完整的字符的。
最初的计算机性能和存储容量都比较差,所以普遍采用4位BCD编码(这个编码出现比计算机还早,最早是用在打孔卡上的)。BCD编码表示数字还可以,但表示字母或符号就很不好用,需要用多个编码来表示。后来又演变出6位的BCD编码(BCDIC),以及至今仍在广泛使用的7位ASCII编码。不过最终决定字节大小的,是大名鼎鼎的System/360。当时IBM为System/360设计了一套8位EBCDIC编码,涵盖了数字、大小写字母和大部分常用符号,同时又兼容广泛用于打孔卡的6位BCDIC编码。System/360很成功,也奠定了字符存储单位采用8位长度的基础,这就是1字节=8位的由来。
ASCII
一切的开始.
ASCII第一次以规范标准的型态发表是在1967年,最后一次更新则是在1986年,至今为止共定义了128个字符;其中33个字符无法显示(一些终端提供了扩展,使得这些字符可显示为诸如笑脸、扑克牌花式等8-bit符号),且这33个字符多数都已是陈废的控制字符。控制字符的用途主要是用来操控已经处理过的文字。在33个字符之外的是95个可显示的字符,包含用键盘敲下空白键所产生的空白字符也算1个可显示字符(显示为空白)。
ASCII 长度是(2的8次方) 也就是 256.
其中33个字符无法显示(一些终端提供了扩展,使得这些字符可显示为诸如笑脸、扑克牌花式等8-bit符号),且这33个字符多数都已是陈废的控制字符
一些奇怪的控制命令. 可能现在已经不怎么用了
从0-31&127
都是控制字符.
从32-126
包含标点,英文字符,运算符号之类的.
如果只是英文的话. 这个是ok的.
可是世界上还有其他的文字.
正好0-127
还剩下128-255
,可以用来扩展字符.
可是你拉丁字母,欧洲的一些语言扩展可以.
亚洲,中文,日文,韩文之类的另一种语系. 就完全没办法了.
于是中国自己扩展了一套.
127
之前的我们都不动. 后面高于127的,两个字符连起来. 组成一个汉字.
每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”,第二个字节称为“低位字节”。
“高位字节”使用了0xA1–0xF7(把01–87区的区号加上0xA0),“低位字节”使用了0xA1–0xFE(把01–94加上0xA0)。 由于一级汉字从16区起始,汉字区的“高位字节”的范围是0xB0–0xF7,“低位字节”的范围是0xA1–0xFE,占用的码位是72*94=6768。其中有5个空位是D7FA–D7FE。
这就是很久以前,我在网吧上的时候,网页编码选项里面的GB2312
可是这个依然不够,生僻汉字. 少数民族也有自己的文字.
字符有一字节和双字节编码,
00
–7F
范围内是第一个字节,和ASCII保持一致,此范围内严格上说有96个文字和32个控制符号。之后的双字节中,前一字节是双字节的第一位。总体上说第一字节的范围是
81
–FE
(也就是不含80
和FF
),第二字节的一部分领域在40
–7E
,其他领域在80
–FE
。
就是说。 第一个字节稍微遵守一下. 必须大于 0x80
.
第二个字节就随你弄了. 介个就是GBK
unicode
ASCII 这么多种扩展. 比如我想浏览其他国家的网站,那就是乱码咯.
除非我有他的编码,而且匹配上了.
做软件啊 之类的更是. 你要卖到其他国家去. 就得各种版本的字符都得弄.
于是有人受不了了 弄了一个UNICODE
定长. 2是两个字节. 也就是(2的16次方) 65536,满足日常需求.
可是也有一个问题.
它保持了以前 ASCII 编码 前 127
位不变. 但是现在是定长 2个字节,所以 前面就只能用 0
来补位. 如果英文传输的话,浪费巨大.
UTF-8
- 对于UTF-8编码中的任意字节B,如果B的第一位为0,则B独立的表示一个字符(ASCII码);
- 如果B的第一位为1,第二位为0,则B为一个多字节字符中的一个字节(非ASCII字符);
- 如果B的前两位为1,第三位为0,则B为两个字节表示的字符中的第一个字节;
- 如果B的前三位为1,第四位为0,则B为三个字节表示的字符中的第一个字节;
- 如果B的前四位为1,第五位为0,则B为四个字节表示的字符中的第一个字节;
以上就是 UTF-8 的实现方式.
有一个原则必须说明,之前百度的时候发现了很多问题,在问 unicode 和 utf-8 的区别.
unicode 是一个字符集(规定你这个 xxxxxxxx yyyyyyy 代表啥)
utf-8 是这个字符集的一种实现编码方式而已.
完全是不同的东西.
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
第一位是0 就是和 ASCII码一样。一个字节.
后面的根据字符范围来分段显示.
第一个字符有多少个1就有多少个字符 + 0. 后面所有字符前面初始都是10.
就拿一个 我 字
unicode
(16) 6211
(2) 110001000010001
转换成 UTF-8
(unicode)110001000010001
(utf-8) 11101100 10010000 106211
这样就转换为utf-8
了
优点
和 ASCII 容.
英文占用少。 便于网络传输.
容易识别,轻易的知道后面字符串的长度。
缺点
中文传输,占用大。
wiki上还说了不少其他缺点。 至少我用不上。
UTF-16
UTF-16是Unicode字符编码五层次模型的第三层:字符编码表(Character Encoding Form,也称为"storage format")的一种实现方式。即把Unicode字符集的抽象码位映射为16位长的整数(即码元)的序列,用于数据存储或传递。Unicode字符的码位,需要1个或者2个16位长的码元来表示,因此这是一个变长表示。
首先 UTF-16 是变长的.
编码方式
小于 U+FFFF
就基于 Unicode 2个字节
大于 U+FFFF
例如U+10437编码(????):
0x10437减去0x10000,结果为0x00437,二进制为0000 0000 0100 0011 0111。
分区它的上10位值和下10位值(使用二进制):0000000001 and 0000110111。
添加0xD800到上值,以形成高位:0xD800 + 0x0001 = 0xD801。 添加0xDC00到下值,以形成低位:0xDC00 +
0x0037 = 0xDC37。
下表总结了该转换,以及其它。颜色指示如何从码点位被分布在所述的UTF-16字节。由UTF-16编码过程中加入附加位以黑色显示。
上面是 wiki 的一个实例。
也就是。
先减去 0x10000
得到结果 0xD800 | (高位10)
和 0xDC00 | 低位(10)
结果就是 两个16进制4位.
说
当然。 在查资料的时候,自己也明白还是有很多东西不明白. 不过基本知识了解了,目前够用了。 以后用到遇到问题再查吧
- 中文占多数的时候使用
UTF-16
因为大部分时候都能固定2个字节,存储效率高.
- E文占多数的时候自然就是
UTF-8
因为 E文只要一个字节
问题来了。
存储文本的时候,很容易区分。
可是网络传输呢.
就算你中文多,图片也是要传输的. 这个也应该要算进去吧.
Gzip压缩后. 是否区别也不大?