惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
DataBreaches.Net
T
Threatpost
N
News and Events Feed by Topic
PCI Perspectives
PCI Perspectives
V2EX - 技术
V2EX - 技术
D
Docker
G
Google Developers Blog
Microsoft Security Blog
Microsoft Security Blog
N
News and Events Feed by Topic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Google Online Security Blog
Google Online Security Blog
The GitHub Blog
The GitHub Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
Y
Y Combinator Blog
M
MIT News - Artificial intelligence
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
T
Troy Hunt's Blog
Webroot Blog
Webroot Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
量子位
Apple Machine Learning Research
Apple Machine Learning Research
H
Help Net Security
F
Full Disclosure
B
Blog
O
OpenAI News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园_首页
Google DeepMind News
Google DeepMind News
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Engineering at Meta
Engineering at Meta
大猫的无限游戏
大猫的无限游戏
Forbes - Security
Forbes - Security
Know Your Adversary
Know Your Adversary
B
Blog RSS Feed
MongoDB | Blog
MongoDB | Blog
Scott Helme
Scott Helme
T
The Exploit Database - CXSecurity.com
博客园 - 聂微东
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The Last Watchdog
The Last Watchdog
Recorded Future
Recorded Future
IT之家
IT之家
Project Zero
Project Zero
Stack Overflow Blog
Stack Overflow Blog
小众软件
小众软件
Attack and Defense Labs
Attack and Defense Labs
L
Lohrmann on Cybersecurity
SecWiki News
SecWiki News
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com

博客园 - Dr.Wang

mongoDB操作命令,摘自官方helper 让PDF文件也支持书签功能 [转]C# 的readonly和const区别 [转]截获asp.net http输出流自己做处理 [转]DES加密 java与.net可以相互加密解密两种方法 [转]JAVA与.NET DES加密解密 Ext树控件的制作 [转]SqlServer行列倒置示例 [转]url传递中文的解决方案总结 - Dr.Wang - 博客园 [转]正则表达式基础知识 - Dr.Wang - 博客园 [转]揭开正则表达式的神秘面纱 [转]正则表达式30分钟入门教程 [转]大流量、高并发网站验证码解决方案 [转]通用分页存储过程 [转]sqlserver2005分页查询 匿名方法解决多线程访问控件的问题 - Dr.Wang - 博客园 [转载]C#自定义事件的步骤介绍 [转载]UDP数据包穿透NAT [转载]UDP数据包大小问题
编码简介
Dr.Wang · 2011-05-11 · via 博客园 - Dr.Wang

最近被字符集搞得头大,基于为自己扫盲的目的,索性收集资料研究一下,现将各方资料归纳成本文。这里并不想把复杂的规则说明一大通。如有需要,请参照其他资料或本文给出的参考资料。 
        如有错误,欢迎指正。 
        [顺便发下牢骚,je的编辑器真TMD难用,排版排得我半死] 

基础知识 

字节和字符 
        字节(octet):顾其英文名而思义,就是一个八位的存储单元,取值范围一定是0~255; 
        字符(character):就是一个语言上的符号,"中"字就是一个字符。字符所占的大小由其编码方式解决,比如"中"在UTF-8中占3个字节(0xE4A8AD),而在GBK中,则占两个字节(0xD6D0)。 

字符集和编码 
        字符集:字符的集合,像Unicode字符集,目标就是收纳了这个世界上所有语言的文字、符号等; 
        字符编码:注意,字符集只是规定了有哪些字符,而最终决定采用哪些字符,每一个字符用多个字节表示等问题,则是由编码来决定的。像Unicode字符集的编码方式有很多,诸如UTF-8、UFT-16、UTF-32等。 
        字符集和字符编码是分开的概念,但有时候称呼上会有些模糊,我们经常笼统地称这些Unicode字符集的编码为Unicode编码。 

内码 
        内码:操作系统内部的字符编码。像早期的DOS采用的是ASCII,而现在的操作系统大把采用Unicode编码。 

编码简史 

        在讲各种编码之前,有必要先讲一个编码这个令人头疼的家伙的历史,这样有助于大家理解今天的编码世界为什么会是这样一个局面。 
        计算机对多语言的支持,大致为分以下三个阶段。 
        阶段一:ASCII时代。计算机是DOS时代的计算内码是ASCII码,ASCII的表示范围就是0到127那几个符号,这意味着,DOS时代的计算机只能显示英文,而无法支持其他语言。没办法,由于英文系国家开创了并继续主导了计算机的世界,他们自然而然地认为全世界的文字用8个字节表示足矣。
        阶段二:ANSI时代。由于上述原因,像我们这些非英文系的国家的为了显示自家的文字,不得不一开始就得面对字符编码的问题,不同国家不同地区都创建了自己的编码标准。像是中国大陆是GB2312及后来的GBK,台湾是BIG5,日本是JIS。ASCII字符集,以及这些由此派生并兼容的字符集称为ANSI字符集。 
        阶段三:Unicode时代。为了和谐而出现,相较于以上两个阶段,这个时代称为国际化时代,适应了跨平台,跨语言之间交换信息的需求。 

Unicode和UTF系列 

Unicode 
        Unicode 字符集收录了这世界上所有的文字符号和特殊符号。对于每一个符号都定义了一个值,称为代码点(code point)。代码点可以用2个字节表示(UCS-2),也可以用4个字节(UCS-4编码)。 

UTF系列 
为什么出现UTF编码? 
        UCS编码虽然定义了每个代码点的编码方式,但是没规定如何传输和存储。比如,在UCS-2码中,英文符号是在ACSII码的前面加上一个0 byte,像"A"的ASCII码 0x41,在UCS码中就是0x0041,这样,对于英文系统来讲会出现大量的0 byte,造成不必要的浪费。而且容易存在对现在ASCII码不兼容的问题。所以这个重担就落在了UTF编码身上,全称是Unicode Transformation Format。 
什么是Endian? 
        我们知道"中"字的UFT-16编码是0x4E,0x2D,但是传输存储的过程中,字节的顺序有可能是(0x4E,0x2D),也可能是(0x2D,0x4E),这就是涉及一个字节序的问题。对于前一种,我们称为Big Endian(大尾,也就是高位在前),而后一总称为Little Endian(小尾,低位在前)。 
        那我们如何知道在不清楚哪一"尾"的情况下进行解析? 
先人已有解决的办法,就是在最前面加多2个字节,OxFEFF表示BE,而0xFFFE表示LE。(注:OxFEFF是实际上不存在的字符,所以正常情况下是不会使用到的,所以,不用担心出现与正常的字符数据冲突的问题),这就是所谓的BOM(Bill Of Material)。 
        UTF系列都存在LE,BE,BOM,无BOM几种版本。 
        比如"中国"的各个版本UTF-16字符编码如下: 

编码字节序列
UTF-16BE4E,2D,56,FD
UTF-16LE2D,4E,fD,56
UTF-16(BOM,BE)FE,FF,4E,2D,56,FD
UTF-16(BOM,LE)FF,FE,2D,4E,fD,56

UFT-8 
        UTF-8采用的是变长码的方式,其编码规则如下: 

代码点值的范围(16进制)第1字节第2字节第3字节
0000 0000-0000 007F0xxxxxxx0-127)
0000 0080-0000 07FF110xxxxx (192-223)10xxxxxx (128-191)
0000 0800-0000 FFFF1110xxxx (224-239)10xxxxxx (128-191)10xxxxxx (128-191)

        注:x的内容是将左边代码点的二进制值依次注入。 
        理论上UTF-8可以达到6个字节编码(上表省略后3位字节以上的编码方式),但实际上,我们一般只采用0x0000 0000 到0x0000 0000FFFF的范围内的字符,也就说UTF-8实际上只采用了3个字节编码。 
        UTF-8除了省空间和兼容ASCII的优点后,其编码方式(类似于哈夫曼编码,很容易判断出1个字节及其后面的字节数)决定了它以下两个优点: 
        1、与字节顺序无关, 可以在不同平台之间交流。 
        2、容错能力高, 任何一个字节损坏后, 最多只会导致一个编码码位损失, 不会链锁错误(如GB码错一个字节就会整行乱码) 

UTF-16和UTF-32 
        UTF-16是变长码,大致上相当于UCS-2码的直接实现,但是也有一部分UCS-4的字符。所以可以猜到,它大部分是采用2个字节编码,而有部分特殊符号采用3字节编码,所以大致相当于20位编码, 值在0到0x10FFFF之间。 
        UTF-32用四个字节表示代码点,这样就可以完全表示UCS-4的所有代码点。 

GB2312、GBK和 GB18030 
        简单来讲,这三者是这样一个关系:GB2312扩展便成了GBK,GBK扩展便成了GB18030。后者都对前者兼容。 
        GB2312:采用2个字节。简体字的编码规范,也包括其他的符号、字母、日文假名等,共7445个图形字符,其中汉字占6763个 
        GBK:采用了2个字节。GB2312明显收录的汉字不够,于是增加了大量不常用汉字,还加入了几乎所有的Big5中的繁体汉字之后便成了GBK。 
        GB18030:与前两者不同,采用了变长的编码方式,有1、2、4个字节的编码长度。1个字节编码与ASCII兼容,2个字节编码与GBK兼容,4个字节主要是收录了少数民族的文字等。GB18030诞生的原因类似于GBK,就是增加了大量的汉字,多收录了藏文、蒙文、维吾尔文等主要的少数民族文字。GB18030现在是国家非手持/非嵌入式设备的强制性标准。 
但是GB18030与前者不同的是,所有的Unicode编码都可以转换为GB18030,而且GB18030除了兼容GBK以及Unicode的BMP部分外,其余的Unicode扩展平面和它的4字节扩展平面都是简单直接的映射 
        其具体映射关系的计算参见《GB18030编码研究以及GBK、GB18030与Unicode的映射》:[http://blog.csdn.net/fmddlmyy/archive/2008/04/13/2288312.aspx]  
        如果说GB2312、GBK是ANSI时代的产物,为什么如今还需要制定GB18030呢?以下引用官方的话:"世界许多国家和地区从方便本国和民族应用的角度出发,制定了相应的编码标准和内码体系,如日本的JIS X 0208和JIS X 0212,韩国的KS C 5601和KS C 5657等,这是国际上采用的通行惯例。制定GB 18030同样符合国际惯例,它全面兼容GB 2312,在字汇上兼容GB 13000.1,可以充分利用已有资源,保证不同系统间的兼容性,最大限度地共享资源,为我国软件产业留有巨大的发展空间。可以相信,GB 18030的实施将有利于国产软件的发展并形成规模,使我国的中文信息技术再上一个台阶。" 

        GB2312、GBK的编码范围如下: 

名称第一字节第二字节
GB23120xA1-0xF7(161-247)0xA1-0xFE(161-254)
GBK0x81-0xFE(129-254)0x40-0xFE(64-254)

        GB18030编码范围如下: 

字节数码位空间
单字节0x00~0x7F (0-127)
双字节第一字节在0x81~0xFE (129-254)第二字节在0x40~0x7E,0×80至0×FE(64-126),(128-254)
四字节第一字节在0x81~0xFE之间 (129-254) 第二字节在0x30~0x39之间 (48-57) 第三字节在0x81~0xFE之间 (129-254) 第四字节在0x30~0x39之间 (48-57)

ASCII和ISO 8859-1 
        ISO 8859-1就比较简单了,我们知道ASCII码是从0x00到0x7F,也就是还有1位没有用到,ISO 8859-1就是在空置的0xA0-0xFF的范围内,加入192个字母及符号,藉以供使用变音符号的拉丁字母语言使用。所以ISO 8859-1又称Latin-1。 

其他编码 
        这里"编码"的含义与以上不同,并不是指字符集的编码,而是一种对文本加工处理的编制方式。因为在开发过程中,也会经常遇到,所以一并介绍。 

application/x-www-form-urlencoded 

        我们在提交表单的时候,常常会看到形如http://localhost:6888/aomstudy/Servlet1?name=%D6%D0%B9%FA 
这样的地址,这些就是application/x-www-form-urlencoded格式编码后的字符串。包括用POST提交表单内容默认是使用这种编码方式。另一种是multipart/form-data,用于有大量非ASCII码文本或二进制数据时,因为这时使用application/x-www-form-urlencoded需要大量的转换,需要耗费太多时间。 
        为什么需要这个application/x-www-form-urlencoded编码?我还不是很明白。个人猜测是:以下规则提到的安全字符都是7位的ASCII字符,虽然HTTP协议支持任意字符,但由于历史原因,在HTTP传输过程,只能保证这7位是安全的,也就是说不被当作其他用途,比如用作控制符等。 
        application/x-www-form-urlencoded的编码规则如下: 
        1.字母数字字符 "a" 到"z"、"A"到 "Z" 和 "0" 到"9" 保持不变。 
        2.特殊字符 "."、"-"、"*" 和"_" 保持不变。 
        3.空格字符 " " 转换为一个加号"+"。 
        4.所有其他字符都是不安全的,因此首先使用一些编码机制将它们转换为一个或多个字节。然后每个字节用一个包含3个字符的字符串"%xy" 表示,其中 xy 为该字节的两位十六进制表示形式。 
        注:以上URL中使用的是GBK编码,原文是http://localhost:6888/aomstudy/Servlet1?name=中国 

Base64 
        介绍之前,先给一段"乱码",让我们有一个感观的认识。 
u7bTrcC0tb1BcHVzaWO1xMrAvec= 
是不是很眼熟?是的,在电子邮件中我们经常见到。Base64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本。主要应用在电子邮件技术、LDIF档案等。 
        Base64怎么编码?简单来讲,Base64不管你使用的是什么编码,UTF-8也好,ASCII也好,它的眼里只有二进制序列,比如说"中国"GBK编码的二进制序列是[11010110],[11010000],[10111001],[11111010]。那么接下Base64 会做以下几件事: 
        一、Base64按照每3个字节一组(共3*8=24位),依次编入4个字节里。 
只使用低位6个位(共4*6=24位),每个字节前两位置0。值的范围在0到63。 
如果不是3的倍数怎么办?就先全部补[01000000](为什么不是0?因为0是有意义的),比如上面最后一个字节[11111010]变成:[00111110],[00100000],[01000000],[01000000],解码时,由于只考虑低6位,而这个值用了第7位,所以不会影响到正常字符的解码。 
        最终转换后成了这样[00110101],[00101101],[00000010],[00111001],[00111110],[00100000],[01000000],[01000000]         
        二、根据编码的值转换成对应的ASCII字符。对应规则如下: 

ASCII字符ASCII字符ASCII字符ASCII字符
0A17R34i51z
1B18S35j520
2C19T36k531
3D20U37l542
4E21V38m553
5F22W39n564
6G23X40o575
7H24Y41p586
8I25Z42q597
9J26a43r608
10K27b44s619
11L28c45t62+
12M29d46u63/
13N30e47v64(pad)=
14O31f48w
15P32g49x
16Q33h50y

         [00110101],[00101101],[00000010],[00111001],[00111110],[00100000],[01000000],[01000000]的十进制值分别经[53],[45],[2],[57],[62],[32],[64],[64],找到相应的ASCII值,最终,我们得到编码后的字符串为1tC5+g== 
         注:以上"乱码"原文为"欢迎来到Apusic的世界"。 



参考资料 
         1.《Unicode详解》:http://tech.idv2.com/2008/02/21/unicode-intro/ 
         2.《Unicode、UCS和UTF编码简介》:http://hi.baidu.com/%D0%DB%CF%D8/blog/item/f3e0d7f221c09c12b17ec512.html 
         3.《GB18030编码研究以及GBK、GB18030与Unicode的映射》:http://blog.csdn.net/fmddlmyy/archive/2008/04/13/2288312.aspx 
         4.《汉字编码问题》:http://www.css8.cn/css8_document/gb2312.htm 
         5.《Java:Unicode简介》:http://tech.it168.com/oldarticle/2006-11-09/200611092313338.shtml 
         6.《字符,字节和编码》:http://www.regexlab.com/zh/encoding.htm 
         7.《ISO 8859-1》:http://baike.baidu.com/view/758577.htm 

         8.《Base64》:http://zh.wikipedia.org/wiki/Base64