BBS.ChinaUnix.net
首页 | 新闻 | Linux | FreeBSD | AIX | Windows | 博客 | 论坛 | 存储 | 网络 | 人才 | Wiki | 资料 | 读书 | 手册 | 下载 | 空间 | 搜索
  会员: 密码: 免费注册 | 忘记密码 | 会员登录 | 搜索 | 帮助 


中文简繁转码的困惑(算是解决了吧)

首页 » 论坛 » Php »  
[打印] [订阅] [收藏] [本帖文本页] [推荐此主题给朋友,立即获积分]
wwdwwd   帅哥
圣骑士




UID:328728
注册:2005-10-28
最后登录: 2008-08-10
帖子:92
精华:0

可用积分:309 (白手起家)
信誉积分:100
专家积分:5 (本版:5)
空间积分:823
推广积分:0

状态:...离线...

[个人空间] [短信] [博客]


1楼 发表于 2008-6-21 23:27 
经过一两天的查资料和思考,算是解决了。我自己参照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])));

QUOTE:
/**
* 主函数,$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;

}

[ 本帖最后由 wwdwwd 于 2008-6-24 01:42 编辑 ]



2008-6-24 01:37
  下载次数: 18
utf8gbbig.zip (15.75 KB)
 


您对本贴的看法:鲜花[0] 臭蛋[0]
积分兑换专区 | IT节能和TPC-E活动获奖名单 | 致电800-858-2903,了解DELL如何为你量身订制笔记本 | 送2G U盘 | 站长如何获得资金?
wwdwwd   帅哥
圣骑士




UID:328728
注册:2005-10-28
最后登录: 2008-08-10
帖子:92
精华:0

可用积分:309 (白手起家)
信誉积分:100
专家积分:5 (本版:5)
空间积分:823
推广积分:0

状态:...离线...

[个人空间] [短信] [博客]


2楼 发表于 2008-6-21 23:28 
对应的码表文件如下:

[ 本帖最后由 wwdwwd 于 2008-6-21 23:30 编辑 ]



2008-6-21 23:30
  下载次数: 17
gbbig.zip (29.8 KB)
 


您对本贴的看法:鲜花[0] 臭蛋[0]
积分兑换专区 | IT节能和TPC-E活动获奖名单 | 致电800-858-2903,了解DELL如何为你量身订制笔记本 | 送2G U盘 | 站长如何获得资金?
wwdwwd   帅哥
圣骑士




UID:328728
注册:2005-10-28
最后登录: 2008-08-10
帖子:92
精华:0

可用积分:309 (白手起家)
信誉积分:100
专家积分:5 (本版:5)
空间积分:823
推广积分:0

状态:...离线...

[个人空间] [短信] [博客]


3楼 发表于 2008-6-22 01:28 
为解开疑惑,我自己简单的改了一下函数,如下:
奇怪的是这两个函数都正常,至少在我测试范围之内正常,我测试了几百M的真实数据。郁闷

QUOTE:
$str = '聯運車輛交接樞紐';       
var_dump(big52gb($str,'GB2312'));exit;

function big52gb($str,$table)

{

        return trans($fp = fopen($table,'r'), $str, $table);

}

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下但不在$from下,故不用在码表里面查找对应的$to编码,直接追加

                                $out .= $t;

                                continue;

                        }

                       

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

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

                        $y =ord($tmp[1]);
                        /**
                                说明在中文的范围内,中文简体的高字节是a1-fe,中文繁体的高字节范围是a0-fe,但真实的编码开始是a1
                                故此范围可确保是中文
                         */
                        if($x>=hexdec('a1') && $x<=hexdec('fe'))
                        {
                                if($x == 161 && $y == 64)
                                {
                                        $out .= $t;continue;
                                }
                                $pos = ($x-160)*510+($y-1)*2;

                       

                                if($pos < 1) {//说明此gb2312字符在此码表中无对应的big5,反之亦然,故不用转,追加

                                        $out .= $t;

                                        continue;

                                }
                                fseek($fp, $pos);

                                $trans= fread($fp,2);

                                $trans = mb_convert_encoding($trans, "UTF-8", $to);
                                $out .= $trans;
                        }

                        else                         {
                        {
                                /**
                                        此处为什么要加这个else呢?按道理说到这里就说明一定是gb2312或big5了,但编码这块有些不是那么准确,
                                        比如'錇'字,它不属于gb2312和big5,但属于gbk和big5-hkscs.但是在$tmp = mb_convert_encoding($t, $from, "UTF-8");
                                        这一步的时候并不返回?,而是返回2个字节的字符串,只是此字符串不再gb2312或big5范围内,是0x83,0x07
                                */
                                $out .= $t;
                        }

                }

                fclose($fp);

                return $out;

}

[ 本帖最后由 wwdwwd 于 2008-6-22 01:30 编辑 ]



您对本贴的看法:鲜花[0] 臭蛋[0]
积分兑换专区 | IT节能和TPC-E活动获奖名单 | 致电800-858-2903,了解DELL如何为你量身订制笔记本 | 送2G U盘 | 站长如何获得资金?
wwdwwd   帅哥
圣骑士




UID:328728
注册:2005-10-28
最后登录: 2008-08-10
帖子:92
精华:0

可用积分:309 (白手起家)
信誉积分:100
专家积分:5 (本版:5)
空间积分:823
推广积分:0

状态:...离线...

[个人空间] [短信] [博客]


4楼 发表于 2008-6-22 20:19 
现在我觉得:如果有一个码表是utf-8下的简体与繁体对应的话,就不需要那么麻烦了,直接对每个utf-8下的汉字查码表找到其对应的的繁体或简体汉字即可,但我在网上找了一下,没找到这样的码表,找到的码表都是gb2312或big5下的,我想自己制作一个这样的码表,却发现找不到utf-8下面汉字编码的规则(utf8里面汉字的范围以及十否连续),有类似经验的兄弟们帮帮忙阿,万分感谢

[ 本帖最后由 wwdwwd 于 2008-6-22 20:40 编辑 ]



您对本贴的看法:鲜花[0] 臭蛋[0]
积分兑换专区 | IT节能和TPC-E活动获奖名单 | 致电800-858-2903,了解DELL如何为你量身订制笔记本 | 送2G U盘 | 站长如何获得资金?
wwdwwd   帅哥
圣骑士




UID:328728
注册:2005-10-28
最后登录: 2008-08-10
帖子:92
精华:0

可用积分:309 (白手起家)
信誉积分:100
专家积分:5 (本版:5)
空间积分:823
推广积分:0

状态:...离线...

[个人空间] [短信] [博客]


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



您对本贴的看法:鲜花[0] 臭蛋[0]
积分兑换专区 | IT节能和TPC-E活动获奖名单 | 致电800-858-2903,了解DELL如何为你量身订制笔记本 | 送2G U盘 | 站长如何获得资金?
folboy
侠客




UID:719968
注册:2008-6-19
最后登录: 2008-07-24
帖子:39
精华:0

可用积分:20 (白手起家)
信誉积分:0
专家积分:0 (本版:0)
空间积分:0
推广积分:0

状态:...离线...

[个人空间] [短信] [博客]


6楼 发表于 2008-6-30 12:39 



您对本贴的看法:鲜花[0] 臭蛋[0]
积分兑换专区 | IT节能和TPC-E活动获奖名单 | 致电800-858-2903,了解DELL如何为你量身订制笔记本 | 送2G U盘 | 站长如何获得资金?
gchanj   帅哥
新手




UID:423715
注册:2006-5-29
最后登录: 2008-10-10
帖子:5
精华:0

可用积分:5 (白手起家)
信誉积分:100
专家积分:0 (本版:0)
空间积分:0
推广积分:0

状态:...离线...

[个人空间] [短信] [博客]


7楼 发表于 2008-7-1 13:21 
utf8下面的简体繁体对应码表有的阿
http://topic.csdn.net/u/20080226 ... e-f77addcb6f5f.html



您对本贴的看法:鲜花[0] 臭蛋[0]
积分兑换专区 | IT节能和TPC-E活动获奖名单 | 致电800-858-2903,了解DELL如何为你量身订制笔记本 | 送2G U盘 | 站长如何获得资金?

首页 » 论坛 » Php »


 


Copyright © 2001-2008 ChinaUnix.net All Rights Reserved     联系我们:

感谢所有关心和支持过ChinaUnix的朋友们    转载本站内容请注明原作者名及出处

京ICP证041476号


清除 Cookies - ChinaUnix - Archiver - WAP - TOP

Processed in 0.098530 second(s), 5 queries , Gzip enabled