喜悦国际村 
» 游客:  注册 | 登录 | 搜索 | 统计 | 喜悦证交所 | 帮助

RSS 订阅当前论坛  

$5.95 Web Hosting     

上一主题 下一主题
 12  1/2  1  2  > 
     
标题: [问题] utf-8下简体繁体转换的思考与困惑  
 
wwdwwd
注册会员
Rank: 2
初级会员



UID 66770
精华 0
积分 76
帖子 55
金钱 76 喜悦币
威望 0
人脉 0
阅读权限 20
注册 2005-9-20
状态 离线
[广告]: Enom域名自助付费 自助注册 自助PUSH 主流域名COM等一律57.99元年
utf-8下简体繁体转换的思考与困惑

工作关系,我需要把一些繁体数据转成简体的,原繁体文件是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;

}


 附件: 您所在的用户组无法下载或查看附件
2008-6-22 07:50 PM#1
查看资料  发短消息  QQ  顶部
 
wwdwwd
注册会员
Rank: 2
初级会员



UID 66770
精华 0
积分 76
帖子 55
金钱 76 喜悦币
威望 0
人脉 0
阅读权限 20
注册 2005-9-20
状态 离线
[广告]: 代充Paypal帐号美元
为解开疑惑,我自己简单的改了一下函数,如下:
奇怪的是这两个函数都正常,至少在我测试范围之内正常,我测试了几百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;

}
2008-6-22 07:50 PM#2
查看资料  发短消息  QQ  顶部
 
wwdwwd
注册会员
Rank: 2
初级会员



UID 66770
精华 0
积分 76
帖子 55
金钱 76 喜悦币
威望 0
人脉 0
阅读权限 20
注册 2005-9-20
状态 离线
[推荐阅读] 给C++初学者的50个忠告
现在我觉得:如果有一个码表是utf-8下的简体与繁体对应的话,就不需要那么麻烦了,直接对每个utf-8下的汉字查码表找到其对应的的繁体或简体汉字即可,但我在网上找了一下,没找到这样的码表,找到的码表都是gb2312或big5下的,我想自己制作一个这样的码表,却发现找不到utf-8下面汉字编码的规则(utf8里面汉字的范围以及十否连续),有类似经验的兄弟们帮帮忙阿,万分感谢
2008-6-22 08:05 PM#3
查看资料  发短消息  QQ  顶部
 
niexa123
注册会员
Rank: 2
中级会员



UID 50950
精华 0
积分 152
帖子 259
金钱 152 喜悦币
威望 0
人脉 0
阅读权限 20
注册 2004-11-18
状态 离线
[推荐阅读] 加密数字
参考下,不错



http://www.ml188.org
2008-6-23 04:48 PM#4
查看资料  访问主页  Blog  发短消息  QQ  顶部
 
奶瓶 (NP博士)
版主
Rank: 7Rank: 7Rank: 7
老仙


UID 52707
精华 4
积分 5559
帖子 6230
金钱 5509 喜悦币
威望 50
人脉 0
阅读权限 100
注册 2004-11-22
来自 北大中文系
状态 离线
[推荐阅读] 深圳PHP招聘
这种事,找个perl包好了~~哈




图片包子,注册送100包子!
2008-6-23 08:57 PM#5
查看资料  访问主页  Blog  发短消息  QQ  ICQ 状态  Yahoo!  顶部
 
wwdwwd
注册会员
Rank: 2
初级会员



UID 66770
精华 0
积分 76
帖子 55
金钱 76 喜悦币
威望 0
人脉 0
阅读权限 20
注册 2005-9-20
状态 离线
[推荐阅读] PHP程序员(急聘)
哎,我在好几个地方都发了求助贴,就是没人理,还是咋村里比较好。经过一两天的查资料和思考,算是解决了。我自己参照utf-8编码规则做了一个utf-8下面简体和繁体的对应码表,然后优化了一下程序,不再使用php的扩展(iconv或mb_convert_encoding之类的函数),发现比我最初的速度提高了三四倍,勉强可以过关了。特附上代码,请有兴趣的兄弟指正一下。我写的代码比较乱,来不及封装或把代码配置话,希望大家能看明白。附件名字是:utf8gbbig.zip

 附件: 您所在的用户组无法下载或查看附件
2008-6-24 01:04 AM#6
查看资料  发短消息  QQ  顶部
 
wwdwwd
注册会员
Rank: 2
初级会员



UID 66770
精华 0
积分 76
帖子 55
金钱 76 喜悦币
威望 0
人脉 0
阅读权限 20
注册 2005-9-20
状态 离线
[推荐阅读] 【北京】招聘初级程序员2名


QUOTE:
原帖由 奶瓶 于 2008-6-23 08:57 PM 发表
这种事,找个perl包好了~~哈
奶瓶说的对,不过我对perl不熟,没办法,只好尝试自己解决,呵呵
2008-6-24 01:05 AM#7
查看资料  发短消息  QQ  顶部
 
xieaotian (老老仙)
版主
Rank: 7Rank: 7Rank: 7


UID 82205
精华 1
积分 1269
帖子 1498
金钱 1269 喜悦币
威望 0
人脉 0
阅读权限 100
注册 2006-10-26
来自 老老神仙部落
状态 在线
[推荐阅读] 符号真是头痛
奶瓶这方面非常厉害的,你多请教下他吧
他的那个imgbox的网站就有简繁转化的功能,做的非常好.




http://www.okpython.com
让中国的python发展的更快。
2008-6-24 07:42 AM#8
查看资料  访问主页  Blog  发短消息  QQ  顶部
 
奶瓶 (NP博士)
版主
Rank: 7Rank: 7Rank: 7
老仙


UID 52707
精华 4
积分 5559
帖子 6230
金钱 5509 喜悦币
威望 50
人脉 0
阅读权限 100
注册 2004-11-22
来自 北大中文系
状态 离线
[推荐阅读] 大连高薪诚聘PHP高手
那个是语言包的~




图片包子,注册送100包子!
2008-6-24 11:50 AM#9
查看资料  访问主页  Blog  发短消息  QQ  ICQ 状态  Yahoo!  顶部
 
奶瓶 (NP博士)
版主
Rank: 7Rank: 7Rank: 7
老仙


UID 52707
精华 4
积分 5559
帖子 6230
金钱 5509 喜悦币
威望 50
人脉 0
阅读权限 100
注册 2004-11-22
来自 北大中文系
状态 离线
[推荐阅读] php连接mssql 2000(局域网机子)
http://search.cpan.org/~audreyt/ ... ncode/HanConvert.pm

这东西是挺有趣的

UTF8,GBK,BIG5,简体繁体,转来转去





图片包子,注册送100包子!
2008-6-24 11:53 AM#10
查看资料  访问主页  Blog  发短消息  QQ  ICQ 状态  Yahoo!  顶部
 12  1/2  1  2  > 
     


  可打印版本 | 推荐给朋友 | 订阅主题 | 收藏主题 | 开通个人空间  


 




Powered by Discuz! 6.1.0  © 2001-2010 Comsenz Inc.
Processed in 0.043340 second(s), 6 queries

(冀ICP备05009913号) 管理员:sadly 邮箱/MSN: sadly@phpx.com QQ:824008(长隐) 清除 Cookies - - Archiver - WAP