赞
踩
参考:
《编码标准-GB2312 GBK GB18030》
《字符编码笔记:ASCII,Unicode 和 UTF-8》
《字体编辑用中日韩汉字Unicode编码表》
《程序员趣味读物:谈谈Unicode编码》
《Unicode 与 UCS》
《细说:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4》
ASCII码使用1个字节记录了128个常用的字符(ASCII规定第一个bit位固定为0,2^7=128),包含控制字符(如:键盘的空格、Tab键),以及打印字符(如:数字、英文字母等)。
看下面的表格:
1980年,为了使每个汉字有一个全国统一的代码,我国颁布了汉字编码的国家标准:GB2312-80《信息交换用汉字编码字符集》基本集,这个字符集是我国中文信息处理技术的发展基础,也是国内所有汉字系统的统一标准。
在这个标准中,我们规定使用两个字节表示一个字符,又为了兼容ASCII码,规定每个字节的首bit位固定为1。这样最终编码后的范围是:0xA1A1 - 0xFEFE
(共94*94=8836个码位
),其中收录了汉字6763个(其中一级汉字3755,二级汉字3008个),覆盖率达到了99.75% 。
其实,在GB2312中还有区位表的概念:
将所有的字符都分为94区,每区又有94位,
- 01-09 区为特殊符号
- 10-15 区为用户自定义符号区(未编码)
- 16-55 区为一级汉字,按拼音排序
- 56-87 区为二级汉字,按部首/笔画排序
- 88-94 区为用户自定义汉字区(未编码)
.
示例如下:
可以通过这里查看完整的区位码列表:《区位码全表》
实际计算机存储的时候肯定不是按照区位码存的(还要避开ASCII的字符嘛),所以GB2312的存储规则如下:
注意:上面的“a”不是ASCII中的a,而是GB2312中的“a”。
另外,我们知道ASCII码的"a"其实就是0x61(即:97,01000001)。
基于以上三个字符的分析,我们新建一个文本文件并输入:“aa啊”,并另存为“ANSI”编码(其实就是GBK编码,GBK兼容GB2312,这里就把GBK当做GB2312),如下:
保存后,我们换个软件打开,观察下16进制,这里我使用editplus,如下:
这里实验的结果和我们分析的结果正好一致。
0x61:表示ASCII中的a
0xA3E1:表示GB2312中的a
0xB0A1:表示汉字“a”
.
注意:
现在已经不用区位码表示了,也不用再考虑区位码到二进制存储的转换了。后面的GBK编码就是直接在GB2312的二进制存储上做的扩展。
对于英文字母和部分标点符号有全角和半角的区别,这是因为这些字母和符号在ASCII中已经定义了一遍,但GB2312中又把这些字母和符号重新定义了一遍(应该是因为中文排版显示不同吧),所以为了区分字母和标点符号究竟是指ASCII中的还是GB2312中的,出现了全角和半角的说法。
而对于汉字来说,是没有全角和半角的区别的,因为ASCII中本就没有汉字。
GBK全称《汉字内码扩展规范》(GBK即“国标”、“扩展”汉语拼音的第一个字母,英文名称:Chinese Internal Code Specification) ,中华人民共和国全国信息技术标准化技术委员会1995年12月1日制订。
指定它的原因?
虽然GB2312中已覆盖了99.75% 的汉字,但仍然有不少生僻字不在规范里面,作为计算机标准不能漏掉这些。
编码特点:
GBK对应的区位码?
从上面GBK的描述中,没有发现区位码的信息,没错,GBK是《汉字内码扩展规范》,也就是说GBK不再使用区位码,而是直接对GB2312的转储二进制进行的扩展。
GBK是如何扩展的GB2312,为什么GB2312最多存储8836个码位,而GBK可以存储23940个?
GBK在扩展GB2312的时候,移除了第二个字节首bit位必须为1的限制,且又做了其他扩展,所以GBK的编码范围是:0x8140 - 0xFEFE,最多能表示的码位:
(0xFE-0x81+1)*(0xFE-0x40+1) => 126*191=24066
然后,GBK又规定去除0x xx7F 一条线,所以GBK最终表示126*190=23940
个码位,共收入 21886 个汉字和图形符号,其中汉字(包括部首和构件)21003 个,图形符号 883 个。
随着计算机的普及,我国后来又在GBK上扩展字符,这被称为GB18030,如:GB18030-2000(2000年发布),GB18030-2005(2005年发布),同时兼容ASCII、GB2312、GBK、基本兼容Unicode,特点如下:
另外,GB18030在微软视窗系统中的代码页为54936。
已被GBK包含。
Big5,又称为大五码或五大码,是使用繁体中文(正体中文)社区中最常用的电脑汉字字符集标准,共收录13,060个汉字。
参考:《大小端(数据在内存中的存储)》
如果有一个编码单元需要用大于1个字节表示,那么要说明这个编码单元放在这几个字节内的顺序是怎样的。
比如说,int类型占用四个字节,那么这四个字节就是一个编码单元,内存中这四个字节排列如下:
小端存储如下(int i=0x00000001; //数字1):
大端存储如下(int i=0x00000001; //数字1):
如果我们在调试中观察内存(使用c++)就能观察到效果了:
如果想使用c#看,需要使用unsafe模式,先设置工程属性:
然后代码如下:
Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位(17*256*256=1114112)
。码位就是可以分配给字符的数字。
Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。UCS可以看作是"Unicode Character Set"的缩写。
前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容,与GB码不兼容。
例如“汉”字的Unicode编码是6C49,而GB码是BABA。
Unicode 在1990年开始研发,1994年正式公布。
2005年3月31日推出的Unicode 4.1.0。
2020年3月10日推出的Unicode 13.0.0。
Unicode 13.0.0的官方文档:
https://www.unicode.org/versions/Unicode13.0.0/UnicodeStandard-13.0.pdf
Unicode的编码范围:
完整的范围,请参考:《百度百科:统一码》
0000-007F:C0控制符及基本拉丁文 (C0 Control and Basic Latin)
:也即ASCII码
0080-00FF:C1控制符及拉丁文补充-1 (C1 Control and Latin 1 Supplement)
0100-017F:拉丁文扩展-A (Latin Extended-A)
0180-024F:拉丁文扩展-B (Latin Extended-B)
0250-02AF:国际音标扩展 (IPA Extensions)
02B0-02FF:空白修饰字母 (Spacing Modifiers)
…此处省略
4E00-9FFF:CJK 统一表意符号 (CJK Unified Ideographs)
:中文汉字大多在这个区
…此处省略
10000–1FFFF: 第1辅助平面,多文种补充平面(Supplementary Multilingual Plane, SMP) [2]
20000–2FFFF: 第2辅助平面,表意文字补充平面(Supplementary Ideographic Plane, SIP) [2]
30000–3FFFF: 第3辅助平面,表意文字第三平面(Tertiary Ideographic Plane, TIP)
40000–DFFFF:第4-13辅助平面,尚未使用
E0000–EFFFF: 第14辅助平面,特别用途补充平面(Supplementary Special-purpose Plane, SSP)
F0000–FFFFF:第15辅助平面,保留作为私人使用区(Private Use Area, PUA)
100000–10FFFF:第16辅助平面,保留作为私人使用区(Private Use Area, PUA)
由于Emoji符号是互联网文化的产物,所以它在Unicode表的后面部分,以下是部分Emoji表情在Unicode表中的排列:
完整的列表,参见Unicode官方文档:《Full Emoji List, v13.1》
上节说到Unicode统一了世界字符的编码标准,但是没有提到这些字符应该怎样转储到计算机中。
Unicode中包含1个字节(如:ASCII码)、两个字节(如:中文)和三个字节(如:第一辅助平面)的长度,为了将Unicode存储到计算机中出现了UCS-2
、UCS-4
、UTF-16
、UTF-32
、UTF-8
几种算法。其中UTF-8
已成事实上的流行者。
UCS-X 和 UTF-X 是国际两个组织搞得标准,后来统一了。
参考:
UCS-2 和 UTF-16编码方式
UCS-2 的编码固定占用2个字节,它包含65536个编码空间。但固定的两个字节不足以覆盖所有的Unicode字符,于是UTF-16诞生了,与UCS-2一样,它使用两个字节为全世界最常用的63K字符编码,不同的是,它使用4个字节对不常用的字符进行编码。UTF-16属于变长编码。
UCS-4 和 UTF-32编码方式
UCS-4的编码固定占用4个字节,编码空间为0x00000000 - 0x7FFFFFFF(可以编码20多亿个字符)。但实际使用范围并不超过0x10FFFF,并且为了兼容Unicode标准,ISO也承诺将不会为超出0x10FFFF的UCS-4编码赋值。由此UTF-32编码被提出来了,它的编码值与UCS-4相同,只不过其编码空间被限定在了0~0x10FFFF之间。因此也可以说:UTF-32是UCS-4的一个子集。
UTF-8
也是使用变长字节表示(1-4个字节表示)。
根据 Unicode 编号的大小,编号小的使用的字节就少,编号大的使用的字节就多。使用的字节个数从 1 到 4 个不等。
UTF-8 的编码规则是:
- ① 对于单字节的符号,字节的第一位设为 0,后面的7位为这个符号的 Unicode 码,。
- ② 对于n字节的符号 (n>1),第一个字节的前 n 位都设为 1,第 n+1 位设为 0,后面字节的前两位一律设为 10,剩下的没有提及的二进制位,全部为这个符号的 Unicode 码 。
UTF-8编码实例:
严的 Unicode 是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800 - 0000 FFFF),因此严的 UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx。然后,从严的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,严的 UTF-8 编码是11100100 10111000 10100101,转换成十六进制就是E4B8A5。
我们在记事本另存为的时候还能看到UTF-16 LE 和 UTF-16 BE的选项,这是因为在制定UTF-16编码的时候允许自己指定字节的存放顺序,这和上面说的大小端存储是一个意思。
UTF-32的和UTF-16一样也有这个特点。
而GBK和UTF-8均没有大小端存储的区别,因为它们都是按照字节的顺序从低位开始排列的。
以汉字 “严” 为例,它的Unicode编码为4E25,我们打开记事本,写入汉字 “严” ,并另存为 UTF-16 LE:
然后,我们使用editplus打开,观察它的16进制如下:
如果,我们另存为UTF-16 BE,那么16进制显示如下:
虽然UTF-8没有大小端存储的区别,但是我们会看到UTF-8-BOM类型的编码,那么有BOM和无BOM的啥区别呢?
带BOM的会在文本的前面添加EF BB BF
三个字节以表示这是UTF-8编码。
还是以汉字“严”为例,我们知道“严”的UTF-8编码为:0xE4B8A5,下面我们打开记事本,输入汉字“严”,将它保存为UTF-8编码:
然后,用editplus打开观察16进制,如下:
如果,我们将它保存为带BOM的UTF-8,然后观察16进制,会发现在首部多了3个字节,如下:
虽然,带BOM的UTF-8编码能更好的表示文本文件,但带bom的shell脚本在linux执行的时候却会报错,所以除非必须,不要使用带BOM的UTF-8编码。
首先,由于GBK不存在字节序,文本前端不需要加字节说明,不带BOM的UTF-8编码文本也不需要加字节说明,所以下面的判断只能是识别已经指明编码格式的文本。
区分字符集和字符编码的概念:
如下图所示:
方法一: 直接在网站:https://www.qqxiuzi.cn/bianma/zifuji.php 上搜索即可得某个字符的 ASCII、GB2312、BIG-5、GBK、GB18030、Unicode编码;
比如,大写英文字母“W”:
又比如汉字“王”:
还有emoji表情:
方法二:上面虽然有常规的编码转换,但是没有关于UTF-16、UTF-8的转换,下面借助c#程序得到字符的UTF-16和UTF-8编码:
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"W Unicode =>{ConvertToHex("W", "Unicode")}");
Console.WriteLine($"W UTF-16 =>{ConvertToHex("W", "UTF-16")}");
Console.WriteLine($"W UTF-32 =>{ConvertToHex("W", "UTF-32")}");
Console.WriteLine($"W UTF-8 =>{ConvertToHex("W", "UTF-8")}");
Console.WriteLine($"王 Unicode =>{ConvertToHex("王", "Unicode")}");
Console.WriteLine($"王 UTF-16 =>{ConvertToHex("王", "UTF-16")}");
Console.WriteLine($"王 UTF-32 =>{ConvertToHex("王", "UTF-32")}");
Console.WriteLine($"王 UTF-8 =>{ConvertToHex("王", "UTF-8")}");
Console.WriteLine($"声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。