字符编码
ASCII, ANSI, Unicode, GBK, GB2312, UTF-8; 437代码页,936代码页,,, 这都是些什么?乱糟糟的。。。 网上海量的说明文章,按我自己的理解,总结一下: 问题产生背景:(基础知识,可跳过)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1. 计算机如何存储 英文字母标点符号这一系列字符? 最简单的思想就是给这一大堆字符规定个顺序,然后用自然数编号(注意:最小的自然数是0), 只让计算机只存储这些字符的数字编号;至于如何在屏幕上呈现给人类的问题交给显示器去做; 2. 各种字符编码集: 我们知道计算机的最小存储单元是1字节共8位,1个存储单元的空间最多可以表示2^8=256种信息状态; 计算机诞生于美国,美国人最开始定义了属于它们自己国家的一套编号"规定" 称为ASCII (American Standard Code for Information Interchange,美国信息互换标准代码) 其中包含了美国人最常用的128个字符(编号从0~127),像美元货币字符$, 英文标点符号,数字, 大小写的英文字母等; ASCII太倾向于美国人,英国人发现它们的货币字符 £ 都没有在这一套编号里, 好在ASCII只占了1个存储单元一半的空间,于是在英国和欧洲不同的国家,为了和ASCII兼容, 它们使用剩下的128~255个数字,表示属于它们民族专有的字符;称为扩展ASCII字符; ASCII太倾向于美国人,扩展ASCII太倾向于非亚洲人;对于中文语言来说,256个数字无法编号所有 汉字,唯一的办法就是扩大存储空间,于是在中国和亚洲不同的国家,它们使用了多个存储单位来编号 自己民族的专有字符;这样诞生了很多属于某个特定国家的“编号规定”,像中国的GB码, 韩国euc-kr编码和日本的Shift_JIS编码等; 这些多字节编码规则只是从本国语言需求而考虑的,一种存储数据在不同的国家,会被计算机识别 为各种信息,这给数据共享带来很大麻烦; 3. 数据共享 为了解决"本地特色"的各种不同编码给数据共享带来的麻烦,Unicode编码诞生了,它在定义编号的时候 不是从某一个国家的民族的需求出发,而是从全人类出发,不分国界,把所有国家,所有民族的字符 都规定一个唯一自然数编号;
各种字符编码集基本概念梳理
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
先说和ASCII兼容的问题: 美国的文档资料很丰富,很重要,如何和他们的ASCII兼容? 扩展ASCII字符集是单字节编码,在特定的国家它们很容易实现和ASCII的兼容,方法就是在1字节的高位做个标记 如果高位为0表示美国人的ASCII字符,高位为1表示本国的特有字符; 对于多字符编码的字符集,在本国该如何实现和ASCII的兼容: 简单的方法是用2个字节,同扩展ASCII字符的做法,每个字节用ASCII字符后剩下的一半的编号, 这样可以表示128*128=16384个字符;在识别的时候,如果发现某一字节的高位是1,则它是一个中文字符的一半, 需要和另一半组合拼成一个完整的汉字;这样避免了识别时的歧义性;我称它为1类扩展ASCII码拼接法 如果超过16384个字符,那怎么办? 我们定义第1字节为扩展ASCII码字符,第2字节编号范围在0~255;这样可以表示128*256 = 32768个字符; 在让计算机识别的时候,从数据的最开始存储单元开始向后扫描,一旦发现有某个字节是扩展ASCII字符, 那么不管它的下一个字符是扩展ASCII码还是ASCII,都作为中文字符的另一半参与拼接; 然后,扫描工作从它的"下下"一个字符继续, 对于ASCII字符不需要做拼接处理;我称它为2类扩展ASCII拼接法; 这两种拼接的方法很简单,都可兼容ASCII;但是都没被采用,原因可能是他们太浪费空间了; 看看GB2312字符集(GB2312是信息交换用汉字编码字符集的标准号省去发布年份1980)的做法: 它占用了94*94 = 8836 个编号,总共收录了包括7455个字符(简体汉字6763个) 8836个编号划分了94区,每个区包含94个编号(称为该区的位号) 分区的思想也许是为了统一管理和方便查询吧,比如中文标点符号放1个区,中文数字序号放在同2区; 然后用第1字节(低字节)表示区号,第2字节(高字节)表示位号;存储的时候为了和ASCII兼容,每个编号+160; 这种存储方法叫EUC(Extended Unix Code)存储; 比如‘啊’字位于16区的1号位,存储的时,编号是:176,161; 转换为16进制表示就是B0,A1; (注:这里A1是高字节) 详细信息请参阅中华人民共和国 国家标准《信息交换用汉字编码字符集--基本集》 GB2312字符集 它通行于大陆,新加波;虽然收录了一些日文,俄文字符,但没有收录少数民族比如藏族语言中的字符; 还有很多古文和生僻字也没收录;这是它的缺陷; BIG-5字符集 它是通行于港台的繁体字符集;共收录13060个字符从A140开始;高字节编码范围是0x81-0xFE, 低字节编码范围是0x40-0x7E和0xA1-0xFE; 0x8140-0xA0FE是保留区域,用于用户造字区。 GBK 是一个局部通用的字符集,该规范收录了ISO 10646.1中的全部 CJK 字符集,并有所补充; (CJK Unified Ideographs中日韩统一表意文字它 是《GB2312-80》、《BIG5》等字符集的超集) GBK的整体编码范围是为0x8140-0xFEFE,不包括低字节是0x7F的组合。 高字节范围是0X81-0xFE,低字节范围是0x40-0x7E和0x80-0xFE。 GBK和GB2312都是双字节等宽编码,如果算上和ASCII兼容所支持的单字节,可以理解为是单字节和双字节混合的变长编码 GB18030 字符集收录了所有Unicode3.1中的字符,包括中国少数民族字符,GBK不支持的韩文字符等等,也可以说是世界 大多民族的文字符号都被收录在内。GB18030编码是变长编码,有单字节、双字节和四字节三种方式。 单字节编码范围是0x00-0x7F,完全等同ASCII; 双字节编码的范围和GBK相同,高字节是0x81-0xFE,低字节的编码范围是0x40-0x7E和0x80-FE; 四字节编码中第一、三字节的编码范围是0x81-0xFE,二、四字节是0x30-0x39。 ANSI 编码: 它是不同系统下各种编码的称谓:没有新增加编码字符集;百度百科上的定义: 不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。 这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。 比如:在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码; 对于ANSI编码而言,0x00~0x7F之间的字符,依旧是1个字节代表1个字符。 代码页: 代码页是字符集编码的别名;不过是给字符集编码编了个号; 代码页分单字节代码页和多字节代码页;扩展ASCII字符在单字节代码页下都是可见的; 437代码页表示美国英语MS-DOS编码 现在936代码页是GBK编码的别名,但又很细微的差别;(以前936代码页是gb2312) 根据微软资料,GBK是对GB2312-80的扩展,也就是CP936字码表 (Code Page 936)的扩展 (之前CP936和GB 2312-80一模一样), GBK自身并非国家标准,只是曾由国家技术监督局标准化司、电子工业部科技与质量监督司公布为“技术规范指导性文件”。 原始GB13000一直未被业界采用,后续国家标准GB18030技术上兼容GBK而非GB13000。 unicode(统一码)编码系统: 可分为编码方式和实现方式两个层次。 目前实际应用的统一码版本对应于UCS-2,使用16位的编码空间。也就是每个字符占用2个字节。 这样理论上一共最多可以表示216(即65536)个字符。基本满足各种语言的使用。实际上目前版本的统一码并未完全使用 这16位编码,而是保留了大量空间以作为特殊使用或将来扩展。 最新(但未实际广泛使用)的统一码版本定义了16个辅助平面,两者合起来至少需要占据21位的编码空间,比3字节略少。 但事实上辅助平面字符仍然占用4字节编码空间,与UCS-4保持一致。未来版本会扩充到ISO 10646-1实现级别3, 即涵盖UCS-4的所有字符。UCS-4是一个更大的尚未填充完全的31位字符集,加上恒为0的首位,共需占据32位,即4字节。 理论上最多能表示2^31个字符,完全可以涵盖一切语言所用的符号。 注: 通用字符集Universal Character Set,UCS,即是由ISO制定的ISO 10646 (或称ISO/IEC 10646)标准所定义的标准字符集。 采用4byte编码. 基本多文种平面的字符的编码为U+hhhh,其中每个h 代表一个十六进制数字,与UCS-2编码完全相同。 而其对应的4字节UCS-4编码后两个字节一致,前两个字节则所有位均为0。 unicode和UCS只是分配整数给字符的编码表;位字符定义了顺序; utf-8, utf-16 是对unicode的编码方法存储的压缩实现; 就像gb2312区位码只定义了94区94位,存储实现的时候为了兼容ASCII给每个字符+160;知识没有压缩数据;
UCS-4编码 | UTF-8字节流 |
---|---|
U+00000000 – U+0000007F | 0xxxxxxx |
U+00000080 – U+000007FF | 110xxxxx 10xxxxxx |
U+00000800 – U+0000FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
U+00010000 – U+001FFFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
U+00200000 – U+03FFFFFF | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
U+04000000 – U+7FFFFFFF | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
参考
http://zh.wikipedia.org/wiki/Unicode http://blog.donews.com/holen/archive/2004/11/30/188182.aspx
平时我们使用windows时,记事本->另存为->“保存”-> “编码”的下拉菜单 中有4个选项,
分别是ANSI, Unicode, Unicode big edian, UTF-8; 稍解释下:
windows XP以后的系统ANSI在简体中文下对应的是GBK编码(请不要贸然否定这句话,我做过考证)
Unicode 统一编码,为了数据共享,
big edian, 大端字节序(高字节对应低位数据),windows默认是小端存储(低字节对应低位数据)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
big endian表示大端存储(高字节存储低位数据),intel模式是小端little endian存储(高字节存储高位数据),比如gb2312编码规定是第1字节存放区码,第2存放位码;"啊"的区码是16, 位码是1;那么用小端方式就是高字节对应1, 低字节对应16了; 什么叫高字节,低字节? 通俗讲:打开记事本,输入0123456,0在内存中对应的就是低字节,1在内存中对应的就是高字节;
UTF-8 Unicode编码的一种实现,节省了存储空间,相当于压缩了unicode编码的压缩方法;
utf-8与unicode的互转:
在实际生活中,经常遇到utf-8, ansi,unicode编码的转换;我们只需要掌握utf-8和unicode的互转,剩下的就很easy,后面你会体会到的
对于gbk字符集来说,我们只需关心u+00000000~u+0000ffff的范围就行了;其他的范围很少能遇到;
从Unicode 到utf8的转换关系表,我们得到下面的小端存储方式, Unicode 转 utf8的 伪代码:
if(数据的高字节==0, 低字节 < 0x80) 输出 低字节; else if(数据的高字节 < 0x08,低字节 >= 0x80) 输出 B(1000 0000)|(数据的0~5位), B(1100 0000)|B(数据的6~11位) else 输出 B(1000 0000)|B(数据的0~5位), B(1000 0000)|B(数据的6~11位), B(1110 0000)|B(数据的12~15) -------------------------- if(data < 0x0080) printf("%c%c", data,0); else if(data >= 0x0080 && data < 0x08ff) printf("%c%c",0x80 | data&0x3F, 0xC0 | (data&0X0FC0)>>6); else printf("%c%c", 0x80|data&0x3F, 0x80|(data&0x0FC0)>>6, 0xE0|(data&0xF000)>>12);
utf8转Unicode的伪代码:
if (d[i]<0x80) print d; else if (d[i+1] > 0xC0) x=(d[i+1]&0x1F)<<6|d[i]&0x3F; printf x&0x00FF , (x&0xFF00)>>8; else x = (d[i+2]&0x0F)<<12|(d[i+1]&0x3F)<<6|d[i]&0x3F; printf x&0x00FF , (x&0xFF00)>>8;
对于utf8和gbk的互转,通过unicode这个桥梁实现;
gbk如何转unicode? 使用cmd/u&type系统调用;
unicode 如何转gbk? 使用系统调用type(默认情况/u开关时关闭的);