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 里面 看到过类似这些函数。没错~~! 理解完上面的编码知识。我相信 这些对你来说~~ 小菜一碟...
- //截取字符串字串-GBK (PHP)
- function gb_substr( $str , $len ){
- $count = 0;
- for ( $i =0; $i < strlen ( $str ); $i ++){
- if ( $count == $len ) break ;
- if (preg_match( "/[\x80-\xff]/" , substr ( $str , $i , 1))) ++ $i ;
- ++ $count ;
- }
- return substr ( $str , 0, $i );
- }
- //统计字符串长度-UTF8 (PHP)
- function utf8_strlen( $str ) {
- $count = 0;
- for ( $i = 0; $i < strlen ( $str ); $i ++){
- $value = ord( $str [ $i ]);
- if ( $value > 127) {
- if ( $value >= 194 && $value <= 223) $i ++;
- elseif ( $value >= 224 && $value <= 239) $i = $i + 2;
- elseif ( $value >= 240 && $value <= 247) $i = $i + 3;
- else die ( 'Not a UTF-8 compatible string' );
- }
- $count ++;
- }
- return $count ;
- }