UTF-8的繁体与简体转换

转自:http://www.cnblogs.com/microtea/archive/2006/05/28/411394.html

本人注曾一直想把编码问题搞清楚,到现在也没有具体研究。若读者有兴趣的话,看看这篇文章,是不是会有所启发?~~~。欢迎高手详解~~~

经过一两天的查资料和思考,算是解决了。我自己参照utf-8编码规则做了一个utf-8下面简体和繁体的对应码表,然后优化了一下程序,不再使用php的扩展(iconv或mb_convert_encoding之类的函数),发现比我最初的速度提高了三四倍,勉强可以过关了。特附上代码,请有兴趣的兄弟指正一下。我写的代码比较乱,来不及封装或把代码配置话,希望大家能看明白。附件名字是:utf8gbbig.zip


工作关系,我需要把一些繁体数据转成简体的,原繁体文件是utf-8格式,转成之后的简体数据文件也要求是utf-8格式的。
整体的思路是这样的:先把utf8的繁体转成big5的繁体,然后再从big5的繁体转成gb2312的简体(利用码表的一一对应关系查找),再从gb2312的简体转成utf8的简体。之所以想到这个思路是因为简体和繁体转换的码表是基于gb2312或big5的,如果说有utf8下面的简体繁体对应码表的话就不用这么麻烦了,直接在utf8下面利用码表就可以一一对应的查找了。如果有哪位朋友以前做过这方面的请指教一下,谢谢。
      具体实现1:先读取一部分utf8的繁体数据,1:用iconv或mb_convert_encoding把它转成big5的繁体;2:利用码表对应关系挨个把这段数据转成gb2312下的简体;3:再利用iconv或mb_convert_encoding把gb2312下的简体转成utf8下的简体。重复执行直到完毕。这样的速度还比较快,但出错。
原因在于:第1步就错了,因为utf8字符集比较大,gb2312或big5字符集比较小,从大到小的转换中如果有些字符在utf8中,但不在gb2312或big5中,则iconv函数是直接截断或转成相似字符或忽略,这显然不对;而mb_convert_encoding遇到此类字符则一律转换成?,这显然更不对,故而这条思路走不通;
     具体实现2:1:先读取一部分utf8的繁体数据;2:挨个获取此utf8数据中的字(可能是汉字或字符)。把每个字转成big5下的字,如果转换完后的长度为1,则说明可能是英文字符,或在utf8里但不在big5的字符,此时把原字符追加到目标字符串后;如果长度等于2说明是big5中的汉字或标点符号等,则根据码表查询与之对应的简体字符。能找到则把对应简体字符转成utf8,追加到目标字符串后,找不到则把原字符追加到目标字符串后;这条思路可以走通。我按照第二个思路找了一个对应的函数,但发现有些东西理解不了,请大家帮忙看看。



到代码的最下面我就不明白了。此处的$trans已经是utf8中的简体中文了,说明它占3个字节。按照这段代码的意思是
                                utf8中的中文的前两个字节与gb2312或big5里面的相同。下面的ifelse判断的意思是:如果这个汉字对应的前两个字节的
                                范围在a1a1到fefe则说明转换正确了,否则还要把原来的$t追加到目标字符串后。a1a1--fefe正好是gb2312的范围
                                但big5的范围是:高字节在a0-fe,低字节是40-7f或a1-fe。此函数如果只是做big5 to gb2312的话也好理解,但它同时也可以处理
                                gb2312 to big5,而且正确,那我就不明白了,求教高手能否解释一下?
                        */

                        $ascii = hexdec(dechex(ord($trans[0])) . dechex(ord($trans[1])));
/**
* 主函数,$str是原字符串,$table是码表文件
*/

function big52gb($str,$table)

{

        return trans(fopen($table, 'r'), $str, "BIG5");

}

function trans(& $fp, $str, $from)

{

                if($from == 'GB2312') {

                        $to = 'BIG5';

                } else {

                        $from = 'BIG5';

                        $to = 'GB2312';

                }

                $out = "";

                for($i = 0; $i < mb_strlen($str, "UTF-8"); $i ++)

                {

                        $t = mb_substr($str, $i, 1, "UTF-8");

                        //如果t的长度<2说明是ascii字符或是汉字的一部分,ascii字符在各种编码中都一致,故而追加到后面即可

                        if(strlen($t)<2)

                        {

                                $out .= $t;

                                continue;

                        }

                        

                        $tmp = mb_convert_encoding($t, $from, "UTF-8");

                        if(strlen($tmp) < 2) {//这块儿<2说明:此字符在utf8下但不在big5下,故不转换直接追加

                                $out .= $t;

                                continue;

                        }

                        

                        //到此说明此utf-8字符在big5下有对应字符

                        $x = ord($tmp[0]);

                        $y =ord($tmp[1]);

                        $pos = ($x-160)*510+($y-1)*2;

                        
                //说明此big5字符在此码表中无对应的gb2312,反之亦然,故不用转,追加,原因应该是big5的字符集比gb2312的字符集大,必然有一些是没有对应的

                        if($pos < 1) {


                                $out .= $t;

                                continue;

                        }

                        

                        //到码表对应位置获取对应的编码字节,此时的编码是$to所指向的编码

                        fseek($fp, $pos);

                        $trans = fread($fp,2);

                        //把gb2312的汉字转成utf8

                        $trans = mb_convert_encoding($trans, "UTF-8", $to);
                        /*
                                        此处很奇怪,明明$tmp>=2说明从utf-8转换过来的是中文字符,通过查找码表的时候也能找到,
                                        就说明能找到对应的$to的中文,但转码为utf-8之后居然占-个字节,因为中文在utf-8里面占用3个字节,
                                        所以此处本来应该占3个字节,据我所知在这里就一个特殊,就是中文空格,在gb2312编码里面是161,64,它在big5或utf8下对应的
                                        字符也都是空格,故直接追加就可以

                                */

                        if(strlen($trans) < 2) {

                                $out .= $t;

                                continue;

                        }
                        /**
                                 到此处我就不明白了。此处的$trans已经是utf8中的简体中文了,说明它占3个字节。按照这段代码的意思是
                                utf8中的中文的前两个字节与gb2312或big5里面的相同。下面的ifelse判断的意思是:如果这个汉字对应的前两个字节的
                                范围在a1a1到fefe则说明转换正确了,否则还要把原来的$t追加到目标字符串后。a1a1--fefe正好是gb2312的范围
                                但big5的范围是:高字节在a0-fe,低字节是40-7f或a1-fe。此函数如果只是做big5 to gb2312的话也好理解,但它同时也可以处理
                                gb2312 to big5,而且正确,那我就不明白了,求教高手能否解释一下?
                        */

                        $ascii = hexdec(dechex(ord($trans[0])) . dechex(ord($trans[1])));

                        if($ascii >= 41377 && $ascii <= 65278) {

                                $out .= $trans;

                        } else {

                                $out .= $t;

                        }

                }

                fclose($fp);

                return $out;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值