- 起因:
最近遇到一件事情,一个接口能够接收传入编码可能是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"; }运行结果:两个不同编码的“中国”,用一个函数mb_convert_encoding就可以自动转换成gbk编码。首页,尝试用utf-8解码,如果出现问题,就会用gbk转码。看来问题解决了,哈哈,可以交差了……
- 问题:
发布后,平静了几天,突然接到反馈:有中文:”袁小”解码出错。⊙﹏⊙b汗 …… ,想……(难道php内置检测模块有问题,或是我哪里欠缺……)⊙﹏⊙b汗…… 看来果然有问题,查询手册:mbstring 模块编码检查,只是识别字符串部分编码,发现与某个字符集匹配上,就认为它属于那种编码。 这不属于它的bug,因为字符串本身没有编码信息标识,没有那个语言能够完全检测通过。
- 问题:
能不能自己写一个检查正则表达式看下到底怎么样呢?要写正则表达式,首先须了解utf8编码规范,查看:http://zh.wikipedia.org/zh/UTF-8目前编码集合只有这样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运行结果:
- 通过上面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编码表)
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="袁小"; 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); }以上是一个折中的方法,在国内一般web程序是:gbk,utf8两种,用上面这个方法基本可以解决问题,可以避免误将gbk识别为utf8,然后将它从utf8->gbk转码,出现”?”号朋友,你有什么更好的方法欢迎交流!!
作者:chengmo QQ:8292669
出处:http://www.cnblogs.com/chengmo
本文版权归作者和博客园共有,欢迎转载,请务必添加原文链接。