您的位置:首页 > 编程语言 > PHP开发

由编码识别遇到问题,思考utf8编码正则表达式(php版本)

2011-02-19 21:59 591 查看
起因:

最近遇到一件事情,一个接口能够接收传入编码可能是utf-8,gbk两种。做过编码方面转换的同学应该知道的,是什么编码不会在字符串里面有什么标记位的。不过utf-8编码有特殊性,因此可以通过正则表达式来检查。只要发现是utf-8编码。就转换,不是utf-8就当gbk处理。编码一些常见问题可以查看:由web程序出现乱码开始挖掘(Bom头、字符集与乱码)

行动:


知道这个原理,马上领任务,开始工作。想到php版本有个mbstring模块可以进行编码检测转换:

<?php
//当前编码是gbk
$str="中国";
$aStrList=array($str,iconv('gbk','utf-8',$str));

foreach($aStrListas$v)
{
echomb_convert_encoding($v,'gbk','utf-8,gbk'),"\r\n";
}


运行结果:





两个不同编码的“中国”,用一个函数mb_convert_encoding就可以自动转换成gbk编码。首页,尝试用utf-8解码,如果出现问题,就会用gbk转码。看来问题解决了,哈哈,可以交差了……



问题:

发布后,平静了几天,突然接到反馈:有中文:”袁小”解码出错。⊙﹏⊙b汗……,想……(难道php内置检测模块有问题,或是我哪里欠缺……)




⊙﹏⊙b汗……看来果然有问题,查询手册:mbstring模块编码检查,只是识别字符串部分编码,发现与某个字符集匹配上,就认为它属于那种编码。这不属于它的bug,因为字符串本身没有编码信息标识,没有那个语言能够完全检测通过。



问题:

能不能自己写一个检查正则表达式看下到底怎么样呢?要写正则表达式,首先须了解utf8编码规范,查看:http://zh.wikipedia.org/zh/UTF-8





目前编码集合只有这样6个维度:php得到维度代码

<?php
//得到utf8字编码各个维度的范围
echobase_convert('1111111',2,16),"\r\n";//维度1
echobase_convert('10000000',2,16),base_convert('10111111',2,16),"\r\n";

echobase_convert('11000000',2,16),base_convert('11011111',2,16),"\r\n";//维度2
echobase_convert('11100000',2,16),base_convert('11101111',2,16),"\r\n";//维度3
echobase_convert('11110000',2,16),base_convert('11110111',2,16),"\r\n";//维度4
echobase_convert('11111000',2,16),base_convert('11111011',2,16),"\r\n";//维度5
echobase_convert('11111100',2,16),base_convert('11111101',2,16),"\r\n";//维度6

运行结果:





通过上面6个维度得到得到对应的正则表达式:

[\x01-\x7f]|[\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3}|[\xf8-\xfb][\x80-\xbf]{4}|[\xfc-\xfd][\x80-\xbf]{5}

以上分别是各个维度范围

<?php
//当前编码是gbk
$str="袁";
echourlencode($str);
echois_utf8($str);
functionis_utf8($str)
{
///utf8编码正则检测函数
///copyrightqq:8292669http://www.cnblogs.com/chengmo$re='/^([\x01-\x7f]|[\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3}|[\xf8-\xfb][\x80-\xbf]{4}|[\xfc-\xfd][\x80-\xbf]{5})+$/';
preg_match($re,$str);
}

上面执行结果返回为1,然后”袁“本身应该是gbk编码。看来上面函数还是不能彻底检查utf8编码。分析原因,从上面正则可以看到,utf8的6个维度对应字节长度从1-6字节。而gbk是1-2个字节。因此他们之间会在1-2个字节长度地方检查出现重合。1个字节的时候gbk与utf8的编码与字符对应关系都一样,但是2个字节时候,对应编码与字符各不相同。


通过查询gbk编码表:http://www.knowsky.com/resource/gb2312tbl.htm进一步确认,范围会在:

[c0-df][a0-bf]之内汉字都会有问题了。如果纯这个范围的汉字组合为字符串就会出现判断不了情况。如果它与其它范围字符组合都可以正确的判断出来。


GBK与UTF8字符集重叠对应的字符是:(gbk编码表)


code+0+1+2+3+4+5+6+7+8+9+A+B+C+D+E+F
C0A0馈愧溃坤昆捆困括扩廓阔垃拉喇蜡
C0B0腊辣啦莱来赖蓝婪栏拦篮阑兰澜谰揽
C1A0痢立粒沥隶力璃哩俩联莲连镰廉怜
C1B0涟帘敛脸链恋炼练粮凉梁粱良两辆量
C2A0隆垄拢陇楼娄搂篓漏陋芦卢颅庐炉
C2B0掳卤虏鲁麓碌露路赂鹿潞禄录陆戮驴
C3A0谩芒茫盲氓忙莽猫茅锚毛矛铆卯茂
C3B0冒帽貌贸么玫枚梅酶霉煤没眉媒镁每
C4A0摹蘑模膜磨摩魔抹末莫墨默沫漠寞
C4B0陌谋牟某拇牡亩姆母墓暮幕募慕木目
C5A0拧泞牛扭钮纽脓浓农弄奴努怒女暖
C5B0虐疟挪懦糯诺哦欧鸥殴藕呕偶沤啪趴
C6A0啤脾疲皮匹痞僻屁譬篇偏片骗飘漂
C6B0瓢票撇瞥拼频贫品聘乒坪苹萍平凭瓶
C7A0恰洽牵扦钎铅千迁签仟谦乾黔钱钳
C7B0前潜遣浅谴堑嵌欠歉枪呛腔羌墙蔷强
C8A0取娶龋趣去圈颧权醛泉全痊拳犬券
C8B0劝缺炔瘸却鹊榷确雀裙群然燃冉染瓤
C9A0伞散桑嗓丧搔骚扫嫂瑟色涩森僧莎
C9B0砂杀刹沙纱傻啥煞筛晒珊苫杉山删煽
CAA0省盛剩胜圣师失狮施湿诗尸虱十石
CAB0拾时什食蚀实识史矢使屎驶始式示士
CBA0恕刷耍摔衰甩帅栓拴霜双爽谁水睡
CBB0税吮瞬顺舜说硕朔烁斯撕嘶思私司丝
CCA0獭挞蹋踏胎苔抬台泰酞太态汰坍摊
CCB0贪瘫滩坛檀痰潭谭谈坦毯袒碳探叹炭
CDA0汀廷停亭庭挺艇通桐酮瞳同铜彤童
CDB0桶捅筒统痛偷投头透凸秃突图徒途涂
CEA0巍微危韦违桅围唯惟为潍维苇萎委
CEB0伟伪尾纬未蔚味畏胃喂魏位渭谓尉慰
CFA0稀息希悉膝夕惜熄烯溪汐犀檄袭席
CFB0习媳喜铣洗系隙戏细瞎虾匣霞辖暇峡
D0A0小孝校肖啸笑效楔些歇蝎鞋协挟携
D0B0邪斜胁谐写械卸蟹懈泄泻谢屑薪芯锌
D1A0选癣眩绚靴薛学穴雪血勋熏循旬询
D1B0寻驯巡殉汛训讯逊迅压押鸦鸭呀丫芽
D2A0摇尧遥窑谣姚咬舀药要耀椰噎耶爷
D2B0野冶也页掖业叶曳腋夜液一壹医揖铱
D3A0印英樱婴鹰应缨莹萤营荧蝇迎赢盈
D3B0影颖硬映哟拥佣臃痈庸雍踊蛹咏泳涌
D4A0浴寓裕预豫驭鸳渊冤元垣袁原援辕
D4B0园员圆猿源缘远苑愿怨院曰约越跃钥
D5A0铡闸眨栅榨咋乍炸诈摘斋宅窄债寨
D5B0瞻毡詹粘沾盏斩辗崭展蘸栈占战站湛
D6A0帧症郑证芝枝支吱蜘知肢脂汁之织
D6B0职直植殖执值侄址指止趾只旨纸志挚
D7A0住注祝驻抓爪拽专砖转撰赚篆桩庄
D7B0装妆撞壮状椎锥追赘坠缀谆准捉拙卓
D8A0亍丌兀丐廿卅丕亘丞鬲孬噩丨禺丿
D8B0匕乇夭爻卮氐囟胤馗毓睾鼗丶亟鼐乜
D9A0佟佗伲伽佶佴侑侉侃侏佾佻侪佼侬
D9B0侔俦俨俪俅俚俣俜俑俟俸倩偌俳倬倏
DAA0凇冖冢冥讠讦讧讪讴讵讷诂诃诋诏
DAB0诎诒诓诔诖诘诙诜诟诠诤诨诩诮诰诳
DBA0邸邰郏郅邾郐郄郇郓郦郢郜郗郛郫
DBB0郯郾鄄鄢鄞鄣鄱鄯鄹酃酆刍奂劢劬劭
DCA0堋堍埽埭堀堞堙塄堠塥塬墁墉墚墀
DCB0馨鼙懿艹艽艿芏芊芨芄芎芑芗芙芫芸
DDA0荨茛荩荬荪荭荮莰荸莳莴莠莪莓莜
DDB0莅荼莶莩荽莸荻莘莞莨莺莼菁萁菥菘
DEA0蕖蔻蓿蓼蕙蕈蕨蕤蕞蕺瞢蕃蕲蕻薤
DEB0薨薇薏蕹薮薜薅薹薷薰藓藁藜藿蘧蘅
DFA0摺撷撸撙撺擀擐擗擤擢攉攥攮弋忒
DFB0甙弑卟叱叽叩叨叻吒吖吆呋呒呓呔呖
只要在这些范围的任意汉字组合一起,都会别解码为utf8,这个也就是utf8编码不能完全识别根本原因。因此需要彻底检查utf8编码,需要排除这些干扰.
整理后的PHP

<?php
//当前编码是gbk本函数以gbk与utf8为例子
$str="袁小";
echocheckUtf8($str);
echocheckUtf8(iconv('gbk','utf-8',$str));

$str="辍辎";
echocheckUtf8($str);
echocheckUtf8(iconv('gbk','utf-8',$str));

/**
*检测字符串是否是utf8编码*
*@paramstring$str输入字符串
*@paramstring$extzh排除重合中文,
*@return1|01是utf80不为utf8
*/
functioncheckUtf8($str,$extzh=1)
{
///utf8编码正则检测函数
///copyrightqq:8292669
///author程默http://www.cnblogs.com/chengmo
//gbk,utf8重叠的范围是:[c0-df][a0-bf]这块字符在utf8中有,在gbk编码没有对应字符因此向gbk转换会出现"?"号
if($extzh==1)
{
$re='/^([\x01-\x7f]|[\xc0-\xdf][\xa0-\xbf])+$/';///这部分字符如果当作utf8处理,在转换为gbk时候就会出现问题"?"号。因此直接返回不为utf8
if(preg_match($re,$str))///公共字符验证成功
{
return0;///不是utf8
}
}
$re='/^([\x01-\x7f]|[\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3}|[\xf8-\xfb][\x80-\xbf]{4}|[\xfc-\xfd][\x80-\xbf]{5})+$/';
returnpreg_match($re,$str);
}






以上是一个折中的方法,在国内一般web程序是:gbk,utf8两种,用上面这个方法基本可以解决问题,可以避免误将gbk识别为utf8,然后将它从utf8->gbk转码,出现”?”号朋友,你有什么更好的方法欢迎交流!!

作者:chengmoQQ:8292669
出处:http://www.cnblogs.com/chengmo
本文版权归作者和博客园共有,欢迎转载,请务必添加原文链接。





                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
章节导航