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

来源:互联网 发布:05年网络流行歌曲 编辑:程序博客网 时间:2024/05/23 11:45

字符是个很麻烦的问题,很早就在网上看到了一系列的好文章了,当时学习完也非常受用,但是不久后就遗忘了。最近遇到一个的需求,在页面固定宽高的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 | 0xxxxxxx0000 0080-0000 07FF | 110xxxxx 10xxxxxx0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx0001 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),可判断编码是否有错误或数据传输过程中是否有错误。

0 0
原创粉丝点击