PHP字符编码ASCII 、GB2312、GBK、UTF-8解释

变量命名规则:
1.  变量名区分大小写(case-sensitive) 。
2.  必须字母或下划线开头。变量名可由 字母、数字、下划线组成。  


看 到这里可能,很多人纳闷了~。那为啥  $我是变量    这样的中文也能做变量名呢? 在PHP里,中文的确是可以做变量名的(能用是能用但千万别项目上用....) 。 因为这里的字母是指: a-z   A-Z  和  扩展ASCII 字符里从 127 到 255  ,16进制表示为:0x7f-0xff   。 那意思就是ASCII字符里从127到255 (0x7f-0xff) 涵括了中文? 的确是这样的。下面就简单的讲下编码。



ASCII 、GB2312、GBK、UTF-8  编码:


ASCII :
ASCII 编码里包括了128个字符。用 十进制 0  到 127 来表示 。那就对了, 0 到 127 不就是 128个字符吗。 每一个数字都代表一个字符。看ASCII 编码表







我 们先看十进制(Dec) 这列,看到了吗 。 十进制数字  9 对应 的字符是 我们开发用得最多的 TAB键。 再看看 48 对应的字符是  0 。没错从  ASCII  48开始 到57 都表示数字  0到9 。  ASCII  97 到 122 表示 小写字母 a 到 z 。   比如: 我们看到的字母 a  ,其实计算机并不认识啥 字母a , 她只认识  97。 她把a 转成了 ASCII 97来进行存储。下面用PHP 来玩玩 ASCII 。



来我们认识两个函数:
ord  ----  Return ASCII value of character     返回 字符的 ASCII 值 。
chr  ----  Return a specific character     返回 ASCII 对应的 字符。

翠花上例子:
<?php echo(ord(‘a‘));?>

//输出   97  
没错吧,  小写a的 ASCII 就是  97。   要把 ASCII 97  对应的字符打印出来:
<?php echo(chr(97));?>


//输出 a
恩。看完基本就明白了。  ASCII 编码 里面包括了  大小写字母   数字 和 一些常用的控制字符。 这样在使用英语的国家基本就能使用了。计算机存储的是ASCII 。 人看到的就是 ASCII 对应的字符。

GB2312 编码:
世界上并不全是用英语作为语言的。比如我国用的是中文。小日本用的是 日语。 韩国用的是韩文。 这些语言和英语完全不是一回事。 你看ASCII 表上有中文对应的”数字“吗?没有吧。因为还有一份 GB2312编码表 ,和ASCII 编码表道理一样。  链接: http://wenku.baidu.com/view/244e2d2ce2bd960590c677a6.html     大家打开一看,哎呀~~! 是否有点乱乱,找不到头绪,啥乱七八糟。 不过当明白原理,就容易看懂了。

在 GB2312编码里面,一个字符我们需要用两个字节来进行存储和表示。我们记得ASCII 编码里面 一个字符只需要一个字节。所以以GB2312存储数据比 ASCII 大一倍。 那么GB2312 这两个字节,分别放啥数字 才能表示字母 a 呢? 我们知道 ASCII 编码  a  就一个字节表示,编码97。 GB2312 编码比 ASCII 复杂一点,


在要看懂 GB2312编码表之前,首先我们要学习下”区位码“。



区位码概念:
GB2312对汉字和其他字符(字母,数字等)进行了“分区”。
       01-09区为特殊符号(数字呀、字母呀等)。
       16-55区为一级汉字,按拼音排序。
       56-87区为二级汉字,按部首/笔画排序。


分区是啥?比如我是广西的,你是河南的、他是广东。 也就是说每个字符肯定存在于某个区里。  这种表示方式叫做 “区位码”。  区位码 其实是   区号和位号(表示一个字符在这个哪个区里的第几列) 。 你想知道每个汉字的区位码?简单呀。给你个链接自己查去  http://www.jscj.com/index/gb2312.php      






我 们以   “啊” 这个汉字来查它的区位码。  以上图所示   1601  是 “啊”这个汉字的区位码。  区号是 16 ,  位号  01。     如果大家还记得,  16区 是 一级汉字哦。牛X呀。一级汉字是啥意思? 我估计是常用的汉字?~我也不知道,这个是国内专家定义的。  位号 是 01   ,位号其实就是说你在这个区里排行老几。  有了 x轴(区号)和y轴(位号) 那么自然就又个交点,通过交点就能在 GB2312编码表上找到对应的汉字了。


如果你现在就去看 GB2312的编码表,我估计你还是看不懂,虽然说通过区位码就能定位到GB2312编码的字符了。但是GB2312编码表上并没那么单纯。 还要继续往下看。

上面我们说的是GB2312的  区位码: 区号和位号。   前面我说过,一个GB2312的字符 是用两个字节来表示的:(高位字节,低位字节)。第一个字节称为“高位字节” ,第二个字节称为“低位字节”  ps:因为高的一般排左边吧~  所以叫 高位字节....    。
算法如下: 一个GB2312的字符  ==  (0xA0 + 区号,0xA0 + 位号)。按照这个算法,你再取看 GB2312编码表,你就看得如鱼得水了。

0xA0 是啥意思呢?  为啥高位字节 等于 区号 加上  0xA0 。  为啥低位字节 等于 位号 加上 0xA0呢。这样组合起来的两个字节就能表示一个 GB2312的字符?  没错,就是那么简单。 0xA0 是一个16进制数字  换算成 十进制其实就等于 160 。   高位字节 等于  160 加上 区号,你可以理解为,其实就是 GB2312编码字符 是从 160 起步的。 就好比 ASCII 编码是从 0开始  到  127 结束。    

我们重新来看下算法  : 一个GB2312的字符  ==  (0xA0 + 区号,0xA0 + 位号) 。
以 上所看,只要我们知道 区位码(区号和位号)就能算出一个汉字的GB2312编码数字。 字母a 的区位码 : 0365 ,也就是区号 03   位号 65 。  按照上面算法我们算下。 把 0xA0 换成十进制  等于 160。 也就是 (160+03,160+65)    等于   (163,225) 换成 16进制(编码表一般都是16进制)  (A3,E1) 。   OK了。   字母a 的gb2312编码 出来了,拿着   A3E1去  GB2312编码表去找这个16进制数字对应的字符吧。 如果你没看错的话,没错就是对应着 编码表上的字母 a 。  
所以, 只要记得 上面公式, 找个工具算出汉字区位码,然后套进公式里面算下。就能的到这个字符的GB2312编码值了。  大家可以自己动手去试试算出上面的汉字 “啊”  的GB2312的编码值。

小总结一下,大家记住:
ASCII 编码的范围  --   十进制 => 0 - 127 。  十六进制: 0x00  -  0x7F 。
GB2312编码的范围  --   十进制 => 高位字节:161 - 247 。十六进制:0xA1 - 0xF7  , 低位字节:  161 - 254 。十六进制:0xA1 - 0xFE  。

GBK 编码:
GB2312 之上的一种扩展编码,GBK 编码已经包括了GB2312编码,并扩展了GB2312编码,使它能表示更多的字符。  GB2312和GBK 原理一样,他们区别只是,编码值范围不一样了。 GBK 更大了。
GB2312 编码值范围 :   高字节从A1到F7,而低位字节从A1到FE。
GBK 编码值范围:  高字节从81到FE,而低位字节从40到FE 。

以上范围可以看出。GBK 比  GB2312大很多。   大是大了很多... 不过现在一般项目都用UTF-8编码了。 接下来将下UTF-8编码方式

UTF-8编码:
世 界上那么多国家,每个国家的语言都不一样。一会出个 ASCII 一会出个 GBK 一会出个 XXOO 编码。那崩溃了。是否能发明一种编码方式,能很好的表示出所有语言呢? Unicode编码就是这样产生的。这里我们只讲Unicode中得一种实现方式。UTF-8,当然还有其他的实现方式。但对于我们WEB开发来说,并不 常用。


ASCII 编码能很好的表示字母、数字等。所以UTF-8 就在它的基础上进行了一下扩展。  按照惯例,我们还是先看下 Unicode编码 表(UTF8编码表?木有。我们需要掌握如何从Unicode 转换成 utf8)

学习这节的目的

  • 掌握从Unicode 转换成utf8编码的方法
  • 判断UTF-8下的字符的字节数。  看下表:
  •  



unicode 字节位表


unicode 编码范围 十进制/十六进制

UTF-8 字节模板二进制/十六进制

字节数

(0)000000 – (127)00007F

0xxxxxxx(00-7F)

一字节

(128)000080 – (2047)0007FF

110xxxxx(C2-DF)      10xxxxxx

两个字节

(2048)000800 – (55295)00D7FF (57344)00E000 – (65535)00FFFF

1110xxxx(E0-EF)     10xxxxxx     10xxxxxx

三个字节

(65536)010000 – (1114111)10FFFF

11110xxx(F0-F4) 10xxxxxx 10xxxxxx 10xxxxxx

四个字节



这个表很重要,记下这个表就基本了解了UTF-8 是怎么一回事。  UTF-8 一共能用四个字节来表示。 但一般字符呢基本用三个字节就能满足了。

一个字节等于8位。这个大家都知道。   从 00000000   -   11111111    这个就是一个字节的数值范围。   换算成十进制就是  0  -  255 。  懂了这个我们继续往下讲。


继续看上图,   我们慢慢讲:

UTF-8中之 一字节:

在UTF-8里面对ASCII 编码进行了保留然后再它之上进行了扩展补充。 一个字节 存的 还是字母呀  数字呀 和ASCII 编码一样。所以 编码范围也是   0  -  127 。
有 点同学纳闷为啥是  127 呢? 一个字节换算成二进制不是 255吗?  因为一字节的时候,第一位给借去了,第一位的值为 固定为0   。大家看上图第一行 “UTF-8字节模板“ 这一列 就明白了。所以其实只有7位是用来表示字符。那么 换算了下 7位  的二进制 就只有   0  -  127  了。 这个几乎和ASCII 编码一样,  想知道0  -  127 都分别对应了什么字符? 看ASCII 编码表呀。

UTF-8 之两字节:
一 个字节 8位,两个字节就16位了。哇!值更大了,能表示的字符更多了。所以什么希腊字母呀、拉丁字母呀等都可以用两字节来表示了。 看 第二行的 ”UTF-8字节模板“ 这一列 。110xxxxx      10xxxxxx          一共有16位, 每8位一个字节。 大家知道,在一个字节的时候,第一位是不能用的。  两个字节的时候稍微不同了。 在两个字节的时候, 第一个字节的前三位给借去了,同时第二个字节的前两位也是给借去了。    恩在这里,我们只要明白一个地方就行。   UTF-8 编码中 当字符是两个字节表示的时候,第一个字节的编码值范围是多少?    第一个字节是 : 110xxxxx   。   那么也就是范围从   11000000  -  11011111     换成十六进制范围是   C2  -   DF  。   恩懂这一点,就足够了。  以后遇到 写 UTF-8编码 下的 截取 函数 、统计长度函数 就不用怕了。



UTF-8 之三字节:
三 个字节表示,是我们用的最多的,因为俺们写中文的嘛。  不过这里注意下就是 ,三字节下的借位情况。  继续看上图 。   1110xxxx(E0-EF)     10xxxxxx     10xxxxxx     看到了吗?你懂的~ 如果还不懂...还是继续重头看起吧。  UTF-8 下 一个字符三字节的。  第一个字节 的范围是多少? 这个必须弄清楚。 范围是从   11100000  -  11101111   十六进制是: E0   -  EF  。  



UTF-8 之四字节:
这个遇到真不多。 不过道理  你真懂了。我就不说了

好了。我们完成了一个目标了 : 判断UTF-8下的字符的字节数。比如以后开发你遇到:
对 于这段文字 :  "逆雪寒之PHP拾遗"        。 我要在UTF-8下统计它的字符长度  和  实现截取字符窜。   应该没那么心慌了。  当然有人说,统计字符长度和截取中文字符窜不是很简单吗?mb_strlen 、 mb_substr 。 的确是可以呀。 但我想我们要知其然知其所以然 。 我们的目标是 PHP产品级研发。 不是 PHP企业网站级研发  -_-! 。



接下来完成另外一个目标: 掌握从Unicode 转换成utf8编码的方法


我 们继续看 unicode 字节位表  。 看第一列    unicode 编码范围 。 四个字节,所以就有四个范围 。 看这个  (0)000000 – (127)00007F       十进制从 0 开始到  127  。这个就是第一字节的unicode 范围。  其他的也是同一个意思。
明白了上面讲的以后,现在开始讲 unicode 编码转换 UTF-8的流程:

我们用 “啊” 这个汉字为例,它的 Unicode 编码是  U+554A (怎么知道的?查 unicode 编码表呀 大哥...) 。 然后我们转成UTF-8 :

  • U+554A 换成十进制是  21834  。 比对  上面的  unicode 字节位表 的第一列  。看到  21834 是在  三个字节的  (2048)000800 – (55295)00D7FF  范围之内。  因为  “啊”  在UTF-8 里是三个字节的。



  • 三个字节 的UTF-8模板  是(看 unicode 字节位表 )   1110xxxx      10xxxxxx     10xxxxxx      。



  • "啊“  的   U+554A换算成二进制是 : 1010  101010 01010



  • 把15位二进制按照顺序的填入(不足最后补0)  三字节的 UTF-8 模板里面  。也就是    11101010  10101010  10010100   。    第三字节 不足位,所以最后补0.



  • 最后结果 ,0xEA  0xAA  0x94  这三个就是 ”啊“  字的UTF-8编码了。




附送: Unicode  编码表  http://wenku.baidu.com/view/01a4feeae009581b6bd9ebe1.html      




终 于讲完了编码。。。 那么回过头来。。讲了那么多废话就是为了解释。 为啥 PHP能用中文来做变量名。 PHP官方文档给的 变量名正则表达式‘[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*‘ 。 里面的\x7f \xff 我想大家都明白了吧。。。 \x7f - \xff 的十进制是: 127 - 255。 那么按照我们之前上面讲的那些 编码规则~ 每个字节的编码 都是在 127 - 255范围之内对吧?除了一字节。 那么也就是说 中文无论是GBK GB2312 还是UTF-8编码的, 用来做变量名都是符合了‘[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*‘ 这条正则的。 接下来我们也不要浪费了前面所学了哪些编码知识。。我们要应用,我们要深入了解...所以开始写两个函数,估计大家都在开源的代码。比如啥 phpcms 啥 uchome discuz 里面 看到过类似这些函数。没错~~! 理解完上面的编码知识。我相信 这些对你来说~~ 小菜一碟...

  1. //截取字符串字串-GBK (PHP)        
  2. function  gb_substr( $str ,  $len ){        
  3.       $count  = 0;        
  4.       for ( $i =0;  $i < strlen ( $str );  $i ++){        
  5.           if ( $count  ==  $len )  break ;        
  6.           if (preg_match( "/[\x80-\xff]/" ,  substr ( $str ,  $i , 1))) ++ $i ;        
  7.            ++ $count ;                
  8.      }        
  9.       return   substr ( $str , 0,  $i );        
  10. }

 

    1. //统计字符串长度-UTF8 (PHP)        
    2. function  utf8_strlen( $str ) {        
    3.       $count  = 0;        
    4.       for ( $i  = 0;  $i  <  strlen ( $str );  $i ++){        
    5.           $value  = ord( $str [ $i ]);        
    6.           if ( $value  > 127) {      
    7.               if ( $value  >= 194 &&  $value  <= 223)  $i ++;        
    8.               elseif ( $value  >= 224 &&  $value  <= 239)  $i  =  $i  + 2;        
    9.               elseif ( $value  >= 240 &&  $value  <= 247)  $i  =  $i  + 3;        
    10.               else   die ( ‘Not a UTF-8 compatible string‘ );        
    11.          }        
    12.           $count ++;        
    13.      }        
    14.       return   $count ;        
    15. }

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。