字符编码学习之一,ASCII,Unicode,UTF-8编码

本文详细介绍了字符编码的历史背景和发展,重点讲解了ASCII、Unicode编码系统的基本原理及其应用场景。针对UTF-8编码,深入解析了其转换规则及如何在计算机中存储和识别多字节字符。

字符是个很麻烦的问题,很早就在网上看到了一系列的好文章了,当时学习完也非常受用,但是不久后就遗忘了。最近遇到一个的需求,在页面固定宽高的div里面显示一段文字,当文字过长时截断加省略号(多行,所以不能简单使用text-overflow属性),于是采用了这个方法,使用等宽字体,算出一行的字符数宽度,然后乘以行数获得总字数在后端用php截取后加省略号返回(注意:是等宽字体,比如黑体[SimHei],一个阿拉伯数字和英文字母,标点占一个字符宽度,汉字占两个字符宽度)。后端php按宽度长截取中文字符时,utf8编码的一个汉字占三个字节,算两个字符宽度,就不能简单的用substr()和mb_substr()来处理了。

今天花了一下午把阮一峰的字符编码相关的文章重新学习了一遍,结合php再重新整理一遍。准备分两个部分,第一部分单讲编码问题,第二部分讲讲php对文字的处理问题。

一,ASCII
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)简称,II不是罗马字母的II啊。使用7个二进制位表示有128个基本的字符,其中33位不可见(0-31,127),其余95位都是可见字符。将一个字节剩下的一个位扩充遍有了额外的128为来组成了EASCII,但是目前有很多互不兼容的EASCII标准。

二,Unicode
使用ASCII远远不能容纳现今这么多国家的语言文字交流需求。于是便设计产生了一套字符集,按一定的规则将人类所有的使用的字符给定唯一的数字编码,这样就再也会有乱码问题了。这套系统设计可容纳100多万字符,目前已经编码了了10万多字符,还在不断的增加,其中有7万多是东亚文化圈产生的字符。(不得不感叹东亚人们造字能力强大啊)。
Unicode,把所有的字符分区存放,称为平面,其中最chong用的字符放在第一个平面称为主平面(BMP)U+0000-FFFF,其他平面称为辅助面U+10000-10FFFF,一个平面可以容纳2^16个字符,一共有17个平面(2^5),所以unicode能够容纳的总字符数为2^(16+5)=2^21个字符。

其中,中文在Unicode排第一的字符是 ‘一’,Unicode码为U+4E00,所有中文字符的编码范围为4E00-9FA5。Unicode这么好用,我们保存,传输数据用它编码就好了。有了Unicode是不是就万无一失了?

并不是,会有两个问题,1,Unicode排前面的编码,比如ASCII,一个字节就可以表示,而上面说的汉字,需要两个字节的编码来表示,还有更大的字符,需要3个或4个字符来表示,计算机如何识别一个串编码到底是1个字节的ASCII字符,还是2个字节的汉字,或者是其他字符来组成的这串编码?比如,’湘’的Unicode编码为U+6E48,又可解释为两个单字节字符为6E:n,48:H,这样就会照成很大的混乱。2,问题1也有简单的解决方法,最长的Unicode需要4个字节表示,那我们把所有的字符都用4字节编码存储,不够4字节的前面补0。这样处理简单粗暴,但是会带来大量的空间浪费,比如存储最常见的英文字母,就会浪费3倍的空间。
于是UTF格式转换出现了。

Unicode的实现方式不同于编码方式。一个字符的Unicode编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式(Unicode Transformation Format,简称为UTF) —-引用维基百科

目前常用的实现方式是UTF-16小端序(LE)、UTF-16大端序(BE)和UTF-8。UTF-8编码,是一种变长编码,它将基本7位ASCII字符仍用7位编码表示,占用一个字节(首位补0)。而遇到与其他Unicode字符混合的情况,将按一定算法转换,每个字符使用1-3个字节编码,并利用首位为0或1进行识别。这样对以7位ASCII字符为主的西文文档就大幅节省了编码长度。

Unicode转换utf8规则
Unicode转utf8规则很简单:
1,如果Unicode是ASCII字符,那么还是用一个字节来表示,第一位是0,其他为填充ASCII编码。
2,如果是多字节编码,在下表的左边根据Unicode编码范围找到对应的UTF-8编码方式,然后将x为用Unicode填充。
或者换一个讲法:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
————来自阮一峰

比如,汉字丁的Unicode编码是U+4E01,在第三行800-FFFF的范围,对应的utf8二进制编码为1110xxxx 10xxxxxx 10xxxxxx,然后将4E01转化二进制0100 1110 0000 0001按位填充,得到1110**0100** 10**111000** 10**000001** 转化为16进制为E4B881,就是’丁’的utf8编码了。

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

注意:为什么上表最后一行不是0001 0000-001F FFFF,因为2003年11月UTF-8被RFC 3629规定,只能使用原来Unicode定义的区域,U+0000到U+10FFFF。所以理论上来说,还可以有5字节,6字节的utf8编码
对应关系为0020 0000 - 03FF FFFF| 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
0400 0000 - 7FFF FFFF| 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

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编码中的任意字节,根据第一位,可判断是否为ASCII字符;根据前二位,可判断该字节是否为一个字符编码的第一个字节;根据前四位(如果前两位均为1),可确定该字节为字符编码的第一个字节,并且可判断对应的字符由几个字节表示;根据前五位(如果前四位为1),可判断编码是否有错误或数据传输过程中是否有错误。

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类的汉字编码与后文的UnicodeUTF-8是毫无关系的。 3、Unicode Unicode字符集(简称为UCS),国际标准组织于1984年4月成立ISO/IEC JTC1/SC2/WG2工作组,针对各国文字、符号进行统一性编码。1991年美国跨国公司成立Unicode Consortium,并于1991年10月与WG2达成协议,采用同一编码字集。目前Unicode是采用16位编码体系,其字符集内容与 ISO10646的BMP(Basic Multilingual Plane)相同。Unicode于1992年6月通过DIS(Draf International Standard),目前版本V2.0于1996公布,内容包含符号6811个,汉字20902个,韩文拼音11172个,造字区6400个,保留 20249个,共计65534个。Unicode编码后的大小是一样的.例如一个英文字母 "a" 和 一个汉字 "好",编码后都是占用的空间大小是一样的,都是两个字节! Unicode可以用来表示所有语言的字符,而且是定长双字节(也有四字节的)编码,包括英文字母在内。所以可以说它是不兼容iso8859-1编码的,也不兼容任何编码。不过,相对于iso8859-1编码来说,uniocode编码只是在前面增加了一个0字节,比如字母'a'为"00 61"。 需要说明的是,定长编码便于计算机处理(注意GB2312/GBK不是定长编码),而unicode又可以用来表示所有字符,所以在很多软件内部是使用unicode编码来处理的,比如java。 Unicode当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表示汉字“严”。具体的符号对应表,可以查询 unicode.org,或者专门的汉字对应表。 http://www.chi2ko.com/tool/CJK.htm
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值