在php程序开发中,有时候要遇到GB2312(GBK)码跟UTF-8之间转换的问题,
尤其是用php进行WAP开发的时候。因为手机能够显示的是UTF-8编码,通过WAP
提交的文字也是用UTF-8编码。为了能够正常显示和处理用户输入的中文,经常
会遇到GB2312(GBK)跟UTF-8之间的转码问题。
目前在网上能够找到GB2312->UTF-8的php示例程序,而UTF-8->GB2312(GBK)
的转码程序我没有找到。另外,Jsp/Java下有现成的转码函数,其实linux平台
下的php也有现成的函数,只是默认php编译的时候没有加载该模块。
基于以上考虑,本人在原有GB2312->UTF-8转码程序的基础上,写了一个
UTF-8到GB的转码程序。另外,在第二部分讲了一下如何配置和使用php自带的
函数来进行转码。
【第一部分】使用GB跟Unicode码表进行转换
汉字的GB2312/GBK(以下简称GB)跟Unicode的汉字编码之间有一个一一对应
的关系,有一张码表列出了他们的对应关系。不过这个码表中GB码跟实际的php
中用bin2hex函数获得GB码值(ASCII)之间差了0x8080,需要做一下处理。
1、GB->UTF-8
中国php联盟上有这样一篇文章写了如何转换,该文章为
http://www.phpx.com/show.php?d=col&i=58
这篇文章就是基于上面的码表来转换的,该码表也在哪有得下载
http://www.phpx.com/download/utf8/gb2312.txt
但是这篇文章只给出了GB->UTF-8和Unicode->UTF8的转码函数,缺少一个
UTF-8到GB的转码函数。参考他的文章,本人写了一个这样的函数。
另外,我将他写的函数用类进行封装了一下,写了一个convert类。因为原
文每次调用转码函数都要读取一次gb2312.txt文件,而I/O操作非常好时间,如
果一个php文件中多次调用这个函数的话,严重影响响应速度。因此用一个类来
封装,在类的构造函数里读取gb2312.txt,这样一个php实例化一个convert,
只需进行一次I/O操作。我做个一个测试,封装好的转码函数,运行10000次需
要3秒,而原来的函数运行100次用了16秒。
该类的部分代码如下:
2.UTF-8->GB
上面给出的那个gb2312是gb跟Unicode之间的对应关系,而UTF-8跟Unicode之
间也有一个转换,要从UTF-8->GB,就必须先从UTF-8->Unicode,再用上面的表来
Unicode->GB.
UTF-8是Unicode的Transformation Format,因为Unicode编码中存在着
0x0034酱紫的字符,这个字符的高位字节是0x00,跟C语言下string的结束标
志/0冲突,会引起判断字符串结束的混乱,因此有了个Transformation
Format ---UTF-8。
从Unicode 到UTF-8的标准可参见RFC-2279,其实主要的一点我们需要知道
下面这张对应的转换关系:
UCS-4 range (hex.) UTF-8 octet sequence (binary)
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
0020 0000-03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
0400 0000-7FFF FFFF 1111110x 10xxxxxx ... 10xxxxxx
这张表左边一列是Unicode编码,右边一列是UTF-8编码。汉字的编码范围在第三
行。这个表的意思(以第三行汉字为例):汉字编码是双字节,比如“爱”的
Unicode编码为0x7231(在上面的gb2312中对应有),她的二进制编码是
0111 0010 0011 0001
根据上面的第三行,取“爱”的二进制后六位(0~5),前面加10(为1011 0001),
再往前第6~11位前再加10(为1000 1000),最高的四位前加1110(为1110 0111),
于是“爱”的UTF-8编码为:
1110 0111 1000 1000 1011 0001 ( 0xe788b1)
那么反过来UTF-8到Unicode的话,只要把UTF-8的二进制的第0~5,8~13,16~19位取
出来组合成双字节的Unicode码就可以了。采用php的位操作(或,与,移位)就
可实现
$unicode = (($utf & 0x0F0000)>>4) | (($utf & 0x3F00)>>2) | ($utf &
0x3F);
获得了Unicode值后,再从上面的gb2312.txt里找到对应的gb2312码,在该码上加
上0x8080(因为上文说过,这个码表跟实际汉字bin2hex出来的值差0x8080,也就是
汉字的最高位是为1)。
于是,在上面的convert类中可以加上一个UTF2GB的函数。代码如下:
注:1、这里定义了一个新的Hashtable(其实是数组),把原来的codetable的
key和value对调了一下,为了方便索引。而我没有把这个数组在Constructor中初始
化,因为UTF-8->GB的函数我调用的比较少,为了程序优化考虑,放在了这里。
2、pack("n",bin_var)函数是把二进制编码转换为字符的一个函数,可参
考php manual.
尤其是用php进行WAP开发的时候。因为手机能够显示的是UTF-8编码,通过WAP
提交的文字也是用UTF-8编码。为了能够正常显示和处理用户输入的中文,经常
会遇到GB2312(GBK)跟UTF-8之间的转码问题。
目前在网上能够找到GB2312->UTF-8的php示例程序,而UTF-8->GB2312(GBK)
的转码程序我没有找到。另外,Jsp/Java下有现成的转码函数,其实linux平台
下的php也有现成的函数,只是默认php编译的时候没有加载该模块。
基于以上考虑,本人在原有GB2312->UTF-8转码程序的基础上,写了一个
UTF-8到GB的转码程序。另外,在第二部分讲了一下如何配置和使用php自带的
函数来进行转码。
【第一部分】使用GB跟Unicode码表进行转换
汉字的GB2312/GBK(以下简称GB)跟Unicode的汉字编码之间有一个一一对应
的关系,有一张码表列出了他们的对应关系。不过这个码表中GB码跟实际的php
中用bin2hex函数获得GB码值(ASCII)之间差了0x8080,需要做一下处理。
1、GB->UTF-8
中国php联盟上有这样一篇文章写了如何转换,该文章为
http://www.phpx.com/show.php?d=col&i=58
这篇文章就是基于上面的码表来转换的,该码表也在哪有得下载
http://www.phpx.com/download/utf8/gb2312.txt
但是这篇文章只给出了GB->UTF-8和Unicode->UTF8的转码函数,缺少一个
UTF-8到GB的转码函数。参考他的文章,本人写了一个这样的函数。
另外,我将他写的函数用类进行封装了一下,写了一个convert类。因为原
文每次调用转码函数都要读取一次gb2312.txt文件,而I/O操作非常好时间,如
果一个php文件中多次调用这个函数的话,严重影响响应速度。因此用一个类来
封装,在类的构造函数里读取gb2312.txt,这样一个php实例化一个convert,
只需进行一次I/O操作。我做个一个测试,封装好的转码函数,运行10000次需
要3秒,而原来的函数运行100次用了16秒。
该类的部分代码如下:
class
convert {
// gb2312和utf-8对应的编码文件
var $filename = " /usr/local/system/phplib/gb2312.txt " ;
var $codetable = array (); // 码表读取后存入这个数组
//构造函数
function convert() {
$tmp = file ( $this -> filename);
while ( list ( $key , $value ) = each ( $tmp ) )
$this -> codetable[ hexdec ( substr ( $value , 0 , 6 ))] =
substr ( $value , 7 , 6 );
}
// GB->UTF-8
function toUTF8( $gb ) {
// 转换过程可参考上面列出的文章,这里就不贴了
return $utf8 ;
}
}
// gb2312和utf-8对应的编码文件
var $filename = " /usr/local/system/phplib/gb2312.txt " ;
var $codetable = array (); // 码表读取后存入这个数组
//构造函数
function convert() {
$tmp = file ( $this -> filename);
while ( list ( $key , $value ) = each ( $tmp ) )
$this -> codetable[ hexdec ( substr ( $value , 0 , 6 ))] =
substr ( $value , 7 , 6 );
}
// GB->UTF-8
function toUTF8( $gb ) {
// 转换过程可参考上面列出的文章,这里就不贴了
return $utf8 ;
}
}
2.UTF-8->GB
上面给出的那个gb2312是gb跟Unicode之间的对应关系,而UTF-8跟Unicode之
间也有一个转换,要从UTF-8->GB,就必须先从UTF-8->Unicode,再用上面的表来
Unicode->GB.
UTF-8是Unicode的Transformation Format,因为Unicode编码中存在着
0x0034酱紫的字符,这个字符的高位字节是0x00,跟C语言下string的结束标
志/0冲突,会引起判断字符串结束的混乱,因此有了个Transformation
Format ---UTF-8。
从Unicode 到UTF-8的标准可参见RFC-2279,其实主要的一点我们需要知道
下面这张对应的转换关系:
UCS-4 range (hex.) UTF-8 octet sequence (binary)
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
0020 0000-03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
0400 0000-7FFF FFFF 1111110x 10xxxxxx ... 10xxxxxx
这张表左边一列是Unicode编码,右边一列是UTF-8编码。汉字的编码范围在第三
行。这个表的意思(以第三行汉字为例):汉字编码是双字节,比如“爱”的
Unicode编码为0x7231(在上面的gb2312中对应有),她的二进制编码是
0111 0010 0011 0001
根据上面的第三行,取“爱”的二进制后六位(0~5),前面加10(为1011 0001),
再往前第6~11位前再加10(为1000 1000),最高的四位前加1110(为1110 0111),
于是“爱”的UTF-8编码为:
1110 0111 1000 1000 1011 0001 ( 0xe788b1)
那么反过来UTF-8到Unicode的话,只要把UTF-8的二进制的第0~5,8~13,16~19位取
出来组合成双字节的Unicode码就可以了。采用php的位操作(或,与,移位)就
可实现
$unicode = (($utf & 0x0F0000)>>4) | (($utf & 0x3F00)>>2) | ($utf &
0x3F);
获得了Unicode值后,再从上面的gb2312.txt里找到对应的gb2312码,在该码上加
上0x8080(因为上文说过,这个码表跟实际汉字bin2hex出来的值差0x8080,也就是
汉字的最高位是为1)。
于是,在上面的convert类中可以加上一个UTF2GB的函数。代码如下:
function
UTF2gb(
$str
) {
if ( ! trim ( $str ) )
return $str ;
$reverse = array ();
while ( list ( $key , $value ) = each ( $this -> codetable) )
$reverse [ hexdec ( $value )] = $key ;
$gb = "" ;
while ( $str ) {
if ( ord ( substr ( $str , 0 , 1 )) > 0xE0 )
{
$tmp = substr ( $str , 0 , 3 );
$str = substr ( $str , 3 , strlen ( $str ));
$utf = hexdec ( bin2hex ( $tmp ));
$unicode = (( $utf & 0x0F0000 ) >> 4 ) | (( $utf & 0x3F00 ) >> 2 )
| ( $utf & 0x3F ) ;
$gb = $gb . pack ( " n " , ( $reverse [ $unicode ]) | 0x8080 );
}
else
{
$gb = $gb . substr ( $str , 0 , 1 );
$str = substr ( $str , 1 , strlen ( $str ));
}
}
return $gb ;
}
if ( ! trim ( $str ) )
return $str ;
$reverse = array ();
while ( list ( $key , $value ) = each ( $this -> codetable) )
$reverse [ hexdec ( $value )] = $key ;
$gb = "" ;
while ( $str ) {
if ( ord ( substr ( $str , 0 , 1 )) > 0xE0 )
{
$tmp = substr ( $str , 0 , 3 );
$str = substr ( $str , 3 , strlen ( $str ));
$utf = hexdec ( bin2hex ( $tmp ));
$unicode = (( $utf & 0x0F0000 ) >> 4 ) | (( $utf & 0x3F00 ) >> 2 )
| ( $utf & 0x3F ) ;
$gb = $gb . pack ( " n " , ( $reverse [ $unicode ]) | 0x8080 );
}
else
{
$gb = $gb . substr ( $str , 0 , 1 );
$str = substr ( $str , 1 , strlen ( $str ));
}
}
return $gb ;
}
key和value对调了一下,为了方便索引。而我没有把这个数组在Constructor中初始
化,因为UTF-8->GB的函数我调用的比较少,为了程序优化考虑,放在了这里。
2、pack("n",bin_var)函数是把二进制编码转换为字符的一个函数,可参
考php manual.