`
zhanchaojiang
  • 浏览: 111954 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

 

  • 起因:

最近遇到一件事情,一个接口能够接收传入编码可能是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 ($aStrList as $v)
{
	echo mb_convert_encoding($v,'gbk','utf-8,gbk'),"\r\n";
}
 
运行结果:
image 
 
两个不同编码的“中国”,用一个函数mb_convert_encoding就可以自动转换成gbk编码。首页,尝试用utf-8解码,如果出现问题,就会用gbk转码。看来问题解决了,哈哈,可以交差了……
 
  1. 问题:
发布后,平静了几天,突然接到反馈:有中文:”袁小”解码出错。⊙﹏⊙b汗 …… ,想……(难道php内置检测模块有问题,或是我哪里欠缺……)
image 
⊙﹏⊙b汗……  看来果然有问题,查询手册:mbstring 模块编码检查,只是识别字符串部分编码,发现与某个字符集匹配上,就认为它属于那种编码。 这不属于它的bug,因为字符串本身没有编码信息标识,没有那个语言能够完全检测通过。 
 
  1. 问题:
能不能自己写一个检查正则表达式看下到底怎么样呢?要写正则表达式,首先须了解utf8编码规范,查看:http://zh.wikipedia.org/zh/UTF-8 

image

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

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

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

运行结果:

image

  1. 通过上面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="";
echo urlencode($str);
echo is_utf8($str);
function is_utf8($str)
{
	///utf8编码正则检测函数
	///copyright qq:8292669  http://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})+$/';
	return 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编码表)

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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编码,需要排除这些干扰.
  1. 整理后的PHP
  2. <?php
    //当前编码是gbk  本函数以gbk与utf8为例子
    $str="袁小";
    echo checkUtf8($str);
    echo checkUtf8(iconv('gbk','utf-8',$str));
    
    $str="辍 辎";
    echo checkUtf8($str);
    echo checkUtf8(iconv('gbk','utf-8',$str));
    
    
    /**
     *检测字符串是否是utf8编码*
     * @param string $str 输入字符串
     * @param string $extzh 排除重合中文,
     * @return 1|0 1是utf8 0不为utf8
     */
    function checkUtf8($str,$extzh=1)
    {
    	///utf8编码正则检测函数
    	///copyright qq: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))  ///公共字符验证成功
    		{
    			return 0;  ///不是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})+$/';
    	return preg_match($re,$str);
    }

image

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

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

1
  
1
  
 
posted on 2011-02-19 21:59 程默 阅读(1846) 评论(13) 编辑 收藏

 

评论

#1楼 2011-02-19 22:29 易海之畔      
这是一段来自thinkphp编码转换的代码,经测试好像解码正常。不制作者能否再次测试一下,有可能本人测试有误。看了作者的文章,受教。。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function auto_charset($fContents,$from='gbk',$to='utf-8'){
    $from   strtoupper($from)=='UTF8'? 'utf-8':$from;
    $to       strtoupper($to)=='UTF8'? 'utf-8':$to;
    if( strtoupper($from) === strtoupper($to) || empty($fContents) || (is_scalar($fContents) && !is_string($fContents)) ){
        //如果编码相同或者非字符串标量则不转换
        return $fContents;
    }
    if(is_string($fContents) ) {
        if(function_exists('mb_convert_encoding')){
            return mb_convert_encoding ($fContents, $to, $from);
        }elseif(function_exists('iconv')){
            return iconv($from,$to,$fContents);
        }else{
            return $fContents;
        }
    }
    elseif(is_array($fContents)){
        foreach ( $fContents as $key => $val ) {
            $_key =     auto_charset($key,$from,$to);
            $fContents[$_key] = auto_charset($val,$from,$to);
            if($key != $_key )
                unset($fContents[$key]);
        }
        return $fContents;
    }
    else{
        return $fContents;
    }

}



分享到:
评论

相关推荐

    更新的自动识别网站编码并修改的php脚本

    php脚本自动批量更改网页编码,识别页面编码并转为utf-8。

    EditPlus 2整理信箱的工具

    这个也算正则表达式的用法,其实仔细看正则表达式应该比较简单,不过既然有这个问题提出,说明对正则表达式还得有个认识过程,解决方法如下 解决: 在替换对话框中,启用“正则表达式”复选框 在查找内容里面输入...

    Editplus 3[1].0

    这个也算正则表达式的用法,其实仔细看正则表达式应该比较简单,不过既然有这个问题提出,说明对正则表达式还得有个认识过程,解决方法如下 解决: 在替换对话框中,启用“正则表达式”复选框 在查找内容里面输入...

    MySQL 5.1参考手册

    10.6. 用于元数据的UTF8 10.7. 与其它DBMS的兼容性 10.8. 新字符集配置文件格式 10.9. 国家特有字符集 10.10. MySQL支持的字符集和校对 10.10.1. Unicode字符集 10.10.2. 西欧字符集 10.10.3. 中欧字符集 10.10.4. ...

    mysql官方中文参考手册

    10.6. 用于元数据的UTF8 10.7. 与其它DBMS的兼容性 10.8. 新字符集配置文件格式 10.9. 国家特有字符集 10.10. MySQL支持的字符集和校对 10.10.1. Unicode字符集 10.10.2. 西欧字符集 10.10.3. 中欧字符集 10.10.4. ...

    MYSQL中文手册

    10.6. 用于元数据的UTF8 10.7. 与其它DBMS的兼容性 10.8. 新字符集配置文件格式 10.9. 国家特有字符集 10.10. MySQL支持的字符集和校对 10.10.1. Unicode字符集 10.10.2. 西欧字符集 10.10.3. 中欧字符集 ...

    MySQL 5.1参考手册中文版

    10.6. 用于元数据的UTF8 10.7. 与其它DBMS的兼容性 10.8. 新字符集配置文件格式 10.9. 国家特有字符集 10.10. MySQL支持的字符集和校对 10.10.1. Unicode字符集 10.10.2. 西欧字符集 10.10.3. 中欧字符集 ...

    MySQL5.1参考手册官方简体中文版

    10.6. 用于元数据的UTF8 10.7. 与其它DBMS的兼容性 10.8. 新字符集配置文件格式 10.9. 国家特有字符集 10.10. MySQL支持的字符集和校对 10.10.1. Unicode字符集 10.10.2. 西欧字符集 10.10.3. 中欧字符集 10.10.4. ...

    mysql5.1中文手册

    用于元数据的UTF8 10.7. 与其它DBMS的兼容性 10.8. 新字符集配置文件格式 10.9. 国家特有字符集 10.10. MySQL支持的字符集和校对 10.10.1. Unicode字符集 10.10.2. 西欧字符集 10.10.3. 中欧...

Global site tag (gtag.js) - Google Analytics