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

时间:2023-01-10 09:46:10
  • 起因:

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

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

目前编码集合只有这样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

运行结果:

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

  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编码表)

 
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);
    }

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

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

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