参考文档:
http://baike.baidu.com/view/443268.htm?fromId=25492
http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
http://tech.idv2.com/2008/02/21/unicode-intro/
http://baike.baidu.com/view/185282.htm
http://baike.baidu.com/view/40801.htm
http://hilojack.sinaapp.com/?p=1291
个人学到的先附加在如下:
ASCII
1. ASCII码只表示128个字符,最前面一位为0. ASCII是计算机的一个基础存储格式。其他存储格式也都为它做了预留。
Unicode与UCS2\UTF8\UTF16
1. Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。UCS-2用两个字节编码,UCS-4用4个字节编码。
我们通常Windows上见到的Unicode,不做说明的情况下,都是指的UCS-2这种字节编码。
2. Unicode UCS2使用两个字节来存储,是我们大多数情况都用到的uniocde方式,代码中使用也很方便(w_char)-双字节与它直接对应起来了。
3. UTF8使用的也非常普遍,它也是一种unicode的编码格式,它存在1/2/3/4个字节来表示一个文字。对于英文字符来说,使用空间非常小。
4. UCS2与UTF16: UCS-2是UTF-16的子集,仅支持Unicode字符平面映射中的基本多文平面.占2个字节. 参考:http://hilojack.sinaapp.com/?p=1291
ANSI与GB2312
1. ANSI: unicode和ansi都是字符代码的一种表示形式,使用1-2字节表示一个字符。ANSI为使计算机支持更多语言,通常使用 0x80~0xFF 范围的 2 个字节来表示 1 个字符。不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。
2. GB2312是一种本地化的编码格式,是ANSI字符代码的一种编码格式,相同的值,在其他本地语言体系里,表达的东西是不同的。《信息交换用汉字编码字符集》是由中国国家标准总局1980年发布,1981年5月1日开始实施的一套国家标准,标准号是GB 2312—1980。
参考:http://baike.baidu.com/view/443268.htm?fromId=25492
下面为转载内容:地址如下
http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
-------------------------------------------------------------
字符编码笔记:ASCII,Unicode和UTF-8
今天中午,我突然想搞清楚Unicode和UTF-8之间的关系,于是就开始在网上查资料。
结果,这个问题比我想象的复杂,从午饭后一直看到晚上9点,才算初步搞清楚。
下面就是我的笔记,主要用来整理自己的思路。但是,我尽量试图写得通俗易懂,希望能对其他朋友有用。毕竟,字符编码是计算机技术的基石,想要熟练使用计算机,就必须懂得一点字符编码的知识。
1. ASCII码
我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从0000000到11111111。
上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为ASCII码,一直沿用至今。
ASCII码一共规定了128个字符的编码,比如空格"SPACE"是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。
2、非ASCII编码
英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。比如,在法语中,字母上方有注音符号,它就无法用ASCII码表示。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。
但是,这里又出现了新的问题。不同的国家有不同的字母,因此,哪怕它们都使用256个符号的编码方式,代表的字母却不一样。比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (ג),在俄语编码中又会代表另一个符号。但是不管怎样,所有这些编码方式中,0--127表示的符号是一样的,不一样的只是128--255的这一段。
至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。一个字节只能表示256种符号,肯定是不够的,就必须使用多个字节表达一个符号。比如,简体中文常见的编码方式是GB2312,使用两个字节表示一个汉字,所以理论上最多可以表示256x256=65536个符号。
中文编码的问题需要专文讨论,这篇笔记不涉及。这里只指出,虽然都是用多个字节表示一个符号,但是GB类的汉字编码与后文的Unicode和UTF-8是毫无关系的。
3.Unicode
正如上一节所说,世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式不一样。
可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是Unicode,就像它的名字都表示的,这是一种所有符号的编码。
Unicode当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表示汉字"严"。具体的符号对应表,可以查询unicode.org,或者专门的汉字对应表。
4. Unicode的问题
需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。
比如,汉字"严"的unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。
这里就有两个严重的问题,第一个问题是,如何才能区别Unicode和ASCII?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果Unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。
它们造成的结果是:1)出现了Unicode的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示Unicode。2)Unicode在很长一段时间内无法推广,直到互联网的出现。
5.UTF-8
互联网的普及,强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种Unicode的实现方式。其他实现方式还包括UTF-16(字符用两个字节或四个字节表示)和UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8是Unicode的实现方式之一。
UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
下表总结了编码规则,字母x表示可用编码的位。
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编码。
已知"严"的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此"严"的UTF-8编码需要三个字节,即格式是"1110xxxx 10xxxxxx 10xxxxxx"。然后,从"严"的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,"严"的UTF-8编码是"11100100 10111000 10100101",转换成十六进制就是E4B8A5。
6. Unicode与UTF-8之间的转换
通过上一节的例子,可以看到"严"的Unicode码是4E25,UTF-8编码是E4B8A5,两者是不一样的。它们之间的转换可以通过程序实现。
在Windows平台下,有一个最简单的转化方法,就是使用内置的记事本小程序Notepad.exe。打开文件后,点击"文件"菜单中的"另存为"命令,会跳出一个对话框,在最底部有一个"编码"的下拉条。
里面有四个选项:ANSI,Unicode,Unicode big endian 和 UTF-8。
1)ANSI是默认的编码方式。对于英文文件是ASCII编码,对于简体中文文件是GB2312编码(只针对Windows简体中文版,如果是繁体中文版会采用Big5码)。
2)Unicode编码指的是UCS-2编码方式,即直接用两个字节存入字符的Unicode码。这个选项用的little endian格式。
3)Unicode big endian编码与上一个选项相对应。我在下一节会解释little endian和big endian的涵义。
4)UTF-8编码,也就是上一节谈到的编码方法。
选择完"编码方式"后,点击"保存"按钮,文件的编码方式就立刻转换好了。
7. Little endian和Big endian
上一节已经提到,Unicode码可以采用UCS-2格式直接存储。以汉字"严"为例,Unicode码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,就是Big endian方式;25在前,4E在后,就是Little endian方式。
这两个古怪的名称来自英国作家斯威夫特的《格列佛游记》。在该书中,小人国里爆发了内战,战争起因是人们争论,吃鸡蛋时究竟是从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开。为了这件事情,前后爆发了六次战争,一个皇帝送了命,另一个皇帝丢了王位。
因此,第一个字节在前,就是"大头方式"(Big endian),第二个字节在前就是"小头方式"(Little endian)。
那么很自然的,就会出现一个问题:计算机怎么知道某一个文件到底采用哪一种方式编码?
Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做"零宽度非换行空格"(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。
如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。
8. 实例
下面,举一个实例。
打开"记事本"程序Notepad.exe,新建一个文本文件,内容就是一个"严"字,依次采用ANSI,Unicode,Unicode big endian 和 UTF-8编码方式保存。
然后,用文本编辑软件UltraEdit中的"十六进制功能",观察该文件的内部编码方式。
1)ANSI:文件的编码就是两个字节"D1 CF",这正是"严"的GB2312编码,这也暗示GB2312是采用大头方式存储的。
2)Unicode:编码是四个字节"FF FE 25 4E",其中"FF FE"表明是小头方式存储,真正的编码是4E25。
3)Unicode big endian:编码是四个字节"FE FF 4E 25",其中"FE FF"表明是大头方式存储。
4)UTF-8:编码是六个字节"EF BB BF E4 B8 A5",前三个字节"EF BB BF"表示这是UTF-8编码,后三个"E4B8A5"就是"严"的具体编码,它的存储顺序与编码顺序是一致的。
9. 延伸阅读
* The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets(关于字符集的最基本知识)
* 谈谈Unicode编码
* RFC3629:UTF-8, a transformation format of ISO 10646(如果实现UTF-8的规定)
以下是转载内容,转载地址:
http://hilojack.sinaapp.com/?p=1291
-----------------------------------------------------------------------------
序
本文试图理清字符编码系统的整体结构.如有理解不对请指正.
字符编码
按照[现代的编码模型],字符编码的主要概念分为:有哪些字符(字符表)、它们的编号(编码字符集)、这些编号如何编码成一系列的“码元”(字符编码表)、这些码元如何组成八位字节流(字符编码方案).
字符表(Character Repotire)
一个系统所有抽象字符的集合,包括我们看得见的汉字/数字/符号和看不见的控制字符.unicode系统所使用的字符集是通用字符集UCS ,由ISO 10646所定义.
编码字符集(CCS:Coded Character Set)
将字符集C映射到非负整数(码位:编码字符的位置).如unicode字符平面映射.即完成对字符的编号.
unicode系统中的码位也叫unicode编码.
将码位转换成有限比特长度的整数值(码元/码值:字符编码的值)
在unicode系统中,其码位可被转换成8位串行的UTF-8,或者16位串行的UTF-16等等.也就说同一码位对应多个码值.
码元(Code Unit,也称“代码单元”)是指一个已编码的文本中具有最短的比特组合的单元。对于UTF-8来说,码元是8比特长;对于UTF-16来说,码元是16比特长;对于UTF-32来说,码元是32比特长。编码长度是码元的整数倍,如UTF-16的长度就是2字节/4字节(一对码元)
我们平时所说的UTF-8,UTF-16都处于字符编码表(CEF)的层面.
字符编码方案(CES:Caracter Encodeing Scheme)
定义如何将码值对应到8位组的串行,以便网络传输和文件存储.
这里有两个大背景:
- 对于多字节的UTF-16来说,windows是先读高字节再读低字节,而MAC则相反.为了标识字节顺序,就选择了一个字节序列标记(BOM:Byte Order Mark)来指定大端序(UTF-16 BE)和小端序(UTF-16).见UTF-16的编码模式
- 有些复杂的编码需要特别的方案:如ISO/IEC 2022需要使用转义串行,如SCSU、BOCU和Punycode需要压缩
在vim中,:%xxd 可查看相关字符的编码 :set fileencoding=**可对字符进行编码转换
unicode编码体系
unicode又名统一码、万国码、单一码、标准万国码
意义
因多语言环境的需要而诞生,它对应于ISO 10646通用字符集UCS,包括了其它所有字符集/已知语言的所有字符.
unicode与iso 10646
史上存在两个尝试创立单一字符集的组织:
- 国际标准化组织ISO——开发了ISO/IEC 10646项目
- 统一码联盟Unicode Consortium——开发了统一码项目
1991年前后,两个项目的参与者都认识到,世界不需要两个不兼容的字符集。于是,它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作.
到现在两个项目仍都存在,两者使用同一字符集,但二者本质上是不同的标准——unicode标准更为丰富,它额外定义了许多与字符相关的语义符号学,并且部分样例字形与iso 10646有显著区别。
unicode的编码和实现
一般,unicode编码系统分为编码方式(CCS)和实现方式(CEF/CES …).
unicode编码方式(CCS)
统一码(unicode)的编码方式使用的是通用字符集UCS. unicode字符的平面映射本质上就是CCS码位映射,即对字符的编号(ISO/IEC 10646-1所定义) (CCS)
这个码位就叫unicode编码.
- UCS-2:包含字符平面映射中的基本多文种平面,占16位,可表达2^16=65536个字符.2. UCS-4:其中已经定义了16个辅助平面.标准规定的UCS-4会占用32个字节,最高字节恒为0,可表达2^31个字符.
平面 |
始末字符值 |
中文名称 |
英文名称 |
0号平面 |
U+0000 – U+FFFF |
基本多文种平面 |
Basic Multilingual Plane, 简称 BMP |
1号平面 |
U+10000 – U+1FFFF |
多文种补充平面 |
Supplementary Multilingual Plane, 简称 SMP |
2号平面 |
U+20000 – U+2FFFF |
表意文字补充平面 |
Supplementary Ideographic Plane, 简称 SIP |
3号平面 |
U+30000 – U+3FFFF |
表意文字第三平面(未正式使用[1]) |
Tertiary Ideographic Plane, 简称 TIP |
4号平面 至 13号平面 |
U+40000 – U+DFFFF |
(尚未使用) |
|
14号平面 |
U+E0000 – U+EFFFF |
特别用途补充平面 |
Supplementary Special-purpose Plane, 简称 SSP |
15号平面 |
U+F0000 – U+FFFFF |
保留作为私人使用区(A区)[2] |
Private Use Area-A, 简称 PUA-A |
16号平面 |
U+100000 – U+10FFFF |
保留作为私人使用区(B区)[2] |
Private Use Area-B, 简称 PUA-B |
实现方式(CEF/CES …)
在unicode编码体系中unicode码位转为实际存储的编码(码值)可以有不同实现方式.比如UTF8/UTF-16/UTF-32
在unicode体系中码位是唯一的,所以字符编码转换程序 在转码时 一般把码值转为unicode再转为其它的编码.
字节顺序标记(BOM)
BOM是用来标记字节序的. 在windows/mac中,UTF-16高低字节的存储顺序是不同的,为了以示区别,特别定义的大尾序和小尾序.同时在文件头部加入一个BOM头(Byte Order Mark).
对于UTF-8来说,它只是一个UTF-8编码记号(不建议使用,它会干扰很多程序的执行)
建议在编辑器中取消bom,比如在vim设置:set nobomb
编码 |
表示 (十六进制) |
表示 (十进制) |
UTF-8 |
EF BB BF |
239 187 191 |
UTF-16(大端序) |
FE FF |
254 255 |
UTF-16(小端序) |
FF FE |
255 254 |
UTF-32(大端序) |
00 00 FE FF |
0 0 254 255 |
UTF-32(小端序) |
FF FE 00 00 |
255 254 0 0 |
UTF-7 |
2B 2F 76 和以下的一个字节:[ 38 | 39 | 2B | 2F ] |
43 47 118 和以下的一个字节:[ 56 | 57 | 43 | 47 ] |
en:UTF-1 |
F7 64 4C |
247 100 76 |
en:UTF-EBCDIC |
DD 73 66 73 |
221 115 102 115 |
en:Standard Compression Scheme for Unicode |
0E FE FF |
14 254 255 |
en:BOCU-1 |
FB EE 28 及可能跟随着FF |
251 238 40 及可能跟随着255 |
GB-18030 |
84 31 95 33 |
132 49 149 51 |
使用UTF-16编码的例子 |
编码名称 |
编码次序 |
编码 |
BOM |
朱 |
, |
聿 |
|
UTF-16LE |
小尾序 |
|
31 67 |
2C 00 |
7F 80 |
69 D8 A5 DE |
UTF-16BE |
大尾序 |
|
67 31 |
00 2C |
80 7F |
D8 69 DE A5 |
UTF-16 |
小尾序,包含BOM |
FF FE |
31 67 |
2C 00 |
7F 80 |
69 D8 A5 DE |
UTF-16 |
大尾序,包含BOM |
FE FF |
67 31 |
00 2C |
80 7F |
D8 69 DE A5 |
UTF-16/UCS-2
UTF-16是Unicode字符集的一种转换方式(Unicode Transfomation Format),它把unicode码位转为16比特长的码元.
字符长度:2个字节(16比特),4个字节(一对16比特码元)
UCS-2是UTF-16的子集,仅支持Unicode字符平面映射中的基本多文平面.占2个字节.
字符编码表(CEF)
对于unicoe基本多文平面(0×0000~0xffff).UTF-16的编码为一个16比特:
UTF-16 == Unicode(0x0~0xffff) #不含(0xd800~0xdfff),这个区保留给UTF-16的前导代理和后导代理
对于uncide辅助平面(0×10000~0x10ffff),UTF-16的编码为一对16比特字符串,由前导代理(lead surrogates)和后导代理(trail surrogates)组成.
将unicode码元(0x10000~0x10ffff)减去0x10000得到20位比特:0x0~0xfffff,这20位比特分高10位A(0~0x3ff)和低10位B(0~0x3ff)
UTF-16(lead surrogates) == A+0xd800 #值范围(0xd800~0xdbff)
UTF-16(trail surrogates) == B+0xd800 #值范围(0xdc00~0xdfff)
UTF-16字符匹配正则
根据UTF-16的CEF规则,我们可以得到关于匹配UTF-16字符的正则伪代码(这个正则是无法执行的,可执行的正则可比这个可复杂多了):
'#[\x{0000}-\x{d7ff}]
|[\x{e000}-\x{ffff}]
|([\x{d800}-\x{dbff}][\x{dc00}-\x{dfff}])#'
UTF-8
utf8以8位为单元对UCS进行编码,编码会占用1~4字节.与utf16所编码的英文字符相比,它的编码长度减少一半.
UTF-8 字符编码表CEF
代码范围 十六进制 |
标量值(scalar value) 二进制 |
UTF-8 二进制/十六进制 |
注释 |
000000 – 00007F 128个代码 |
00000000 00000000 0zzzzzzz |
0zzzzzzz(00-7F) |
ASCII字符范围,字节由零开始 |
七个z |
七个z |
000080 – 0007FF 1920个代码 |
00000000 00000yyy yyzzzzzz |
110yyyyy(C0-DF) 10zzzzzz(80-BF) |
第一个字节由110开始,接着的字节由10开始 |
三个y;二个y;六个z |
五个y;六个z |
000800 – 00D7FF 00E000 – 00FFFF 61440个代码 [Note 1] |
00000000 xxxxyyyy yyzzzzzz |
1110xxxx(E0-EF) 10yyyyyy 10zzzzzz |
第一个字节由1110开始,接着的字节由10开始 |
四个x;四个y;二个y;六个z |
四个x;六个y;六个z |
010000 – 10FFFF 1048576个代码 |
000wwwxx xxxxyyyy yyzzzzzz |
11110www(F0-F7) 10xxxxxx 10yyyyyy 10zzzzzz |
将由11110开始,接着的字节由10开始 |
三个w;二个x;四个x;四个y;二个y;六个z |
三个w;六个x;六个y;六个z |
UTF-8优缺点
优点
- 保证搜索时一个字符的字符串不会出现在另一个字符的串里面.
- 兼容ASCII
- 抗干扰和稳定性好:一段两字节随机串行碰巧为合法的UTF-8而非ASCII的机率为32分1。对于三字节串行的机率为256分1,对更长的串行的机率就更低了
缺点
- 与UTF-16/gbk 想比,处理CJK字符串,编码长度不占优势
UTF-8正则匹配
当使用Perl时,可用以下的表达式测试页面是否使用了UTF-8编码:
m/\A(
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*\z/x;
utf8_unicode_ci和utf8_general_ci区别
utf8_unicode_ci的最主要的特色是支持扩展,即当把一个字母看作与其它字母组合相等时。例如,在德语和一些其它语言中‘ß’等于‘ss’。
utf8_general_ci是一个遗留的 校对规则,不支持扩展。它仅能够在字符之间进行逐个比较。这意味着utf8_general_ci校对规则进行的比较速度很快,但是与使用utf8_unicode_ci的校对规则相比,比较正确性较差)。
其它UTF
UTF-7
UTF-7是一种可变长度的字符编码方式,用以将UTF-16字符以ASCII编码.也就是说用修改的Base64(Modified Base64)去编码UTF-16字符.
因为SMTP作为基本邮件传输标准,只允许传输ASCII字符,过去很多邮件传输都使用UTF-7.自从MIME扩展了电子邮件标准之后,SMTP支持了其它字符集.现在绝大多数邮件服务商都使用UTF-8/GB2312/GB18030作为邮件字符编码.
严格来说 UTF-7 不能算是 Unicode 所定义的字符集之一,较精确的来说, UTF-7 是提供了一种将 Unicode 转换为 7 比特 US-ASCII 字符的转换方式
Modified Base64 与Base64的主要区别是结尾不会有”=”
UTF-7由于安全性薄弱,已经走入历史.
Gmail中文用户默认外发邮件编码(content-type)是GB2312(可改为UTF-8).其传输编码(Content-Transfer-Encoding)会按数据最短的原则选择: quoted-printable/base64或者不用.
腾讯邮箱用户默认的外发邮件编码是gb18030(可改为UTF-8).其传输编码使用的是base64.
UTF-32
UTF-32 是一个 UCS-4 的子集,使用32-比特的码值,只在0到10FFFF的字码空间(百万个码位)
内容传输编码(Content-Transfer-Encoding)
CTE由MIME定义,用于email数据传输.包括“7bit”,“8bit”,“binary”,“quoted-printable”,“base64”.其中常见的传输码为Base64/quoted-printable
Base64
Base64不是字符编码方案,而是一种基于64个可打印字符来表示二进制数据的表示方法.6bit(2^6=64)为一单元,对应一个可打印的字符.三个字节有24个位元.对应4个Base64字符.
编码规则
将二进制流/文本流以每6bit为一单元,3个字节为一组.以6bit(2^6=64)的数字大小为位置对应以下字符中的一个:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
如果最后剩余2个或1个字节,按以下方式补0:
文本 |
M |
a |
n |
ASCII编码 |
77 |
97 |
110 |
二进制位 |
0 |
1 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
索引 |
19 |
22 |
5 |
46 |
Base64编码 |
T |
W |
F |
u |
最后,如果剩下两个字节,在编码结果后加1个“=”;如果最后剩下一个字节,编码结果后加2个“=”;如果没有剩下任何数据,就什么都不要加,这样才可以保证资料还原的正确性。
Quoted-printable
可打印字符引用编码(Quoted-printable,或QP encoding).
它使用ASCII字符表示各种字符编码–以便能个7bit or 8bit 数据通路上传输数据.
此编码为是MIME中 content transfer encoding的一种,用于email.(与base64并列为两种基本的邮件传输编码)
编码规则
Quoted-printable以8bit为单位进行编码,规则如下:
一般的8bit编码为"="加两个十六进制值,如"\x7A"编码为"=7A"
对于可打印的ascii码:0x21-0x7E("="号:0x3D除外),可用ASCII码直接表示
但是,如果水平制表符和空格符出现在行尾,必须用OP编码表示为"=09"(tab)和"=20"(space)
如果QP编码的数据每行长度超过76个字符,QP编码结果的每行结尾加一个软换行("=")
Gmail在发送以下字符时所使用的Content-Transfer-Econding正是QP,如果将其ContentType设置为:UTF-8.并发送邮件内容(“新”的utf8编码为\xe696b0):
新This is a test!
If you believe that truth=beauty, then surely mathematics is the most beautiful branch of philosophy.
那么,经过QP编码后的邮件text/plain原文为:
=E6=96=B0This is a test!
If you believe that truth=3Dbeauty, then surely mathematics is the most bea=
utiful branch of philosophy.
你很可能看到的是这个(因为你的邮件文本编辑器做format字符时就在most处断行了,使得单行不超过76个字符):
=E6=96=B0This is a test!
If you believe that truth=3Dbeauty, then surely mathematics is the most
beautiful branch of philosophy.
还有一个text/html原文.
与base64相比,如果Content-Type编码与ASCII是兼容的,那么QP编码后的邮件原文中ASCII是可读的.
GB相关的编码
本小节主要归纳汉字相关的编码:GB2312,GBK,GB18030.他们的关系如下图:
如图所示,GB18030基本兼容GBK.
GB2312
GB2312,也叫GB2312-80,于1981年推出.
特点
- 收录6763个汉字
编码结构
先对汉字进行分区(得到的编码叫区位码).
- 01-09区为特殊符号。
- 16-55区为一级汉字,按拼音排序。
- 56-87区为二级汉字,按部首/笔画排序。
编码:
对于ascii(0×00-0x7f)保持不变.
对于汉字和符号,使用两个字节表示:“高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0),“低位字节”使用了0xA1-0xFE(把01-94加上0xA0)
“啊”的区位码是0×1001,对应的gb2312就是0xb0a1
GBK
背景
因为原GB2312字符不足, 厂商微软利用GB 2312-80未使用的编码空间,收录GB 13000.1-93全部字符制定了GBK编码.
编码结构
对于GB2312字符保持不变,仅对Gb2312未使用的编码区进行了扩充.
对于双字节来说:第一字节的范围是81–FE(也就是不含80和FF),第二字节的一部分领域在40–7E,其他领域在80–FE.
GBK的编码范围
范围 |
第1字节 |
第2字节 |
编码数 |
字数 |
水准 GBK/1 |
A1 –A9 |
A1 –FE |
846 |
717 |
水准 GBK/2 |
B0 –F7 |
A1 –FE |
6,768 |
6,763 |
水准 GBK/3 |
81 –A0 |
40 –FE (7F 除外) |
6,080 |
6,080 |
水准 GBK/4 |
AA –FE |
40 –A0 (7F 除外) |
8,160 |
8,160 |
水准 GBK/5 |
A8 –A9 |
40 –A0 (7F 除外) |
192 |
166 |
用户定义 |
AA –AF |
A1 –FE |
564 |
|
用户定义 |
F8 –FE |
A1 –FE |
658 |
|
用户定义 |
A1 –A7 |
40 –A0 (7F 除外) |
672 |
|
合计: |
|
|
23,940 |
21,886 |
按拼音排序
基于Gbk/Gb2312是按音序来编码的.可用此规则来对汉字进行拼音排序(以php为例)
//按拼音首字母排序
$arr = array(
'北京'=>'010',
'成都'=>'028',
);
ukrsort($arr, 'cmp');
var_dump($arr);
/**
*
* 比较拼音首字母(基于字符是按拼音顺序编码)
*/
function cmp(&$a, &$b) {
$a = iconv('utf-8', 'gbk', $a);
$a = $a[0];
$b = iconv('utf-8', 'gbk', $b);
$b = $b[0];
if ($a == $b) {
return 0;
}
return ($a > $b) ? 1 : -1;
}
function ukrsort(&$arr, $func) {
foreach ($arr as $k => $v) {
if (is_array($arr[$k])) {
ukrsort($arr[$k], $func);
}
}
uksort($arr, $func);
}
也可以通过编码找到绝大部分汉字的拼音首字母(以php为例)
/**
* 获取汉字拼音首字母(基于字符是按拼音顺序编码)
*/
function getFirstLetter($str) {
$fchar = ord($str{0});
if ($fchar >= ord("A") and $fchar <= ord("z"))
return strtoupper($str{0});
if (!is_string($str)) {
var_dump($str);
return;
}
$s1 = @iconv("UTF-8", "gbk", $str);
$s2 = @iconv("gbk", "UTF-8", $s1);
if ($s2 == $str) {
$s = $s1;
} else {
$s = $str;
}
$asc = ord($s{0}) * 256 + ord($s{1}) ;
if ($asc >= 45217 and $asc <= 45252)
return "A";
if ($asc >= 45253 and $asc <= 45760)
return "B";
if ($asc >= 45761 and $asc <= 46317)
return "C";
if ($asc >= 46318 and $asc <= 46825)
return "D";
if ($asc >= 46826 and $asc <= 47009)
return "E";
if ($asc >= 47010 and $asc <= 47296)
return "F";
if ($asc >= 47297 and $asc <= 47613)
return "G";
if ($asc >= 47614 and $asc <= 48118)
return "I";
if ($asc >= 48119 and $asc <= 49061)
return "J";
if ($asc >= 49062 and $asc <= 49323)
return "K";
if ($asc >= 49324 and $asc <= 49895)
return "L";
if ($asc >= 49896 and $asc <= 50370)
return "M";
if ($asc >= 50371 and $asc <= 50613)
return "N";
if ($asc >= 50614 and $asc <= 50621)
return "O";
if ($asc >= 50622 and $asc <= 50905)
return "P";
if ($asc >= 50906 and $asc <= 51386)
return "Q";
if ($asc >= 51387 and $asc <= 51445)
return "R";
if ($asc >= 51446 and $asc <= 52217)
return "S";
if ($asc >= 52218 and $asc <= 52697)
return "T";
if ($asc >= 52698 and $asc <= 52979)
return "W";
if ($asc >= 52980 and $asc <= 53688)
return "X";
if ($asc >= 53689 and $asc <= 54480)
return "Y";
if ($asc >= 54481 and $asc <= 55289)
return "Z";
return null;
}
GB18030
GB 18030,全称:国家标准GB 18030-2005《信息技术 中文编码字符集》,是中华人民共和国现时最新的内码字集(2005年发布).
有以下特点:
采用多字节编码,每个字可以由1个、2个或4个字节组成。
支持全部unicode(UCS)全部统一汉字.收录范围包含繁体汉字以及日韩汉字70244个
与GBK基本兼容,与GB 2312完全兼容
编码结构
- 单字节,其值从0到0x7F。
- 双字节,第一个字节的值从0×81到0xFE,第二个字节的值从0×40到0xFE(不包括0x7F)。
- 四字节,第一/三字节的值从0×81到0xFE,第二/四字节的值从0×30到0×39.
正则匹配
'#[\x00-\x7f]|[\x81-\xfe][\x40-0xfe]|([\x81-0xfe][\x30-\x39]){2}#'
参考
- 维基UCS
- 维基字符平面映射
- utf_bom
- 维基unicode
- 维基现代编码模型
- 维基UTF-16
- 维基UTF-8
- 维基GB2312
- 维基GBK
- 维基GB18030