PHP的文件编码一般都是默认的UTF-8。而面对GBK的数据库的话,接入数据库不仅要指定字符集是gbk,得到的结果中,汉字部分也还是需要从GBK转编码到UTF-8的。

对于编码检测与转换这个问题,我梳理自己负责的一个系统相关方法,以码会友,于是,就有了这篇进化史。

本文是完结篇,承接昨天的博客,我们加入长度判定,得到了第五版代码。

第五版(调优第三版方法)

function stringToUtf8($str)
{
$ret = $str;
if ($str!="") {
$encode = mb_detect_encoding($str, [ "EUC-CN", "UTF-8" ]);
#如果未检测出编码类型,或者是中文或者Json转失败都强制转换
if (!$encode || $encode=="EUC-CN" || json_encode([$str])===false) {
$ret = mb_convert_encoding($str, "UTF-8", "EUC-CN");
}
}
if (strlen($ret) <= strlen($str) ) {
return $str;
} else {
return $ret;
}
}

测试用例:

<?php
$arr = ["影", "小鸠", "露营", "鸭寮街", "欧洲", "甄嬛ABC"];
foreach ($arr as $ret) {
test($ret);
}

function test($ret)
{
$ret = mb_convert_encoding($ret, "GBK", "UTF-8");
$ret = stringToUtf8($ret);
echo "{$ret}\n";
$ret = stringToUtf8($ret);
echo "{$ret}\n";
}

输出结果:

影
影
小鸠
小鸠
露营
露营
鸭寮街
鸭寮街
欧洲
娆ф床
ABC
ABC

你以为完美了?结果并不是,测试妹子雪娇发现说,索引异常,跟入诊断发现是“甄嬛ABC”造成的问题。恩~~~~看输出结果,也确实有问题。如何继续?

进化思路
此时,浩哥丢了个链接:PHP判断字符串编码函数mb_detect_encoding总结。阅读后发现,iconv这函数检测竟然比mb_detect_encoding稳定准确。那试试水好了。


第六版(调优第五版方法)

function stringToUtf8($str)
{
$ret = $str;
if ($str!="") {
$encode = detect_encoding($str);
#如果是中文或者Json转失败都强制转换
if ($encode=="EUC-CN" || json_encode([$str])===false) {
$ret = mb_convert_encoding($str, "UTF-8", "EUC-CN");
}
}
return $ret;
}

function detect_encoding($str)
{
if ($str === @iconv('EUC-CN', 'EUC-CN//IGNORE', $str)) {
return 'EUC-CN';
}
return 'UTF-8';
}

输出结果:

影
影
小鸠
小鸠
露营
露营
鸭寮街
鸭寮街
欧洲
娆ф床
甄??BC
甄??BC

比第五版好一点,就是甄嬛ABC竟然能正常进入索引了!只不过索引里显示为“甄??BC”。然后,测试妹子又发现,UTF-8编码的欧洲是会被重复编码的(第五版也一样)。
跪了,还有完没完啊……

精神病人思路广的进化思路
一步一步来,首先,看起来嬛这个字不太对劲,测试发现以GBK为目标编码才能正常转为UTF-8的有效可识别的嬛字。之前都是从EUC-CN转UTF-8,那下一版就从这里入手。


第七版(调优第六版方法)

function stringToUtf8($str)
{
$ret = $str;
if ($str!="") {
$encode = detect_encoding($str);
#如果是中文或者Json转失败都强制转换
if ($encode=="EUC-CN" || json_encode([$str])===false) {
$ret = mb_convert_encoding($str, "UTF-8", "GBK");
}
}
return $ret;
}

function detect_encoding($str)
{
if ($str === @iconv('EUC-CN', 'EUC-CN//IGNORE', $str)) {
return 'EUC-CN';
}
return 'UTF-8';
}

输出结果:

影
影
小鸠
小鸠
露营
露营
鸭寮街
鸭寮街
欧洲
娆ф床
甄嬛ABC
甄嬛ABC

嬛字的问题解决了~~~但是欧洲依然会被重复编码~~~~

精神病人思路广的进化思路
那= =检测编码的时候,我额外判定下是否包含中文汉字,如果不包含,说明这货的编码肯定不是UTF-8。


第八版(调优第七版方法)

function stringToUtf8($str)
{
$ret = $str;
if ($str!="") {
$encode = detect_encoding($str);
#如果是中文或者Json转失败都强制转换
if ($encode=="EUC-CN" || json_encode([$str])===false) {
$ret = mb_convert_encoding($str, "UTF-8", "GBK");
}
}
return $ret;
}

function detect_encoding($str)
{
if ($str === @iconv('EUC-CN', 'EUC-CN//IGNORE', $str) && preg_match('/[\x{4e00}-\x{9fa5}]/u', $str)===0) {
return 'EUC-CN';
}
return 'UTF-8';
}

输出结果:

影
影
小鸠
小鸠
露营
露营
鸭寮街
鸭寮街
欧洲
欧洲
甄嬛ABC
甄嬛ABC

喜大普奔,终于,终于所有用例都通过了。一个简单的编码检测并转换,折腾了半天。只能说基础太差,技术太菜~

至此,任务完成,这一次我绝对不会被打脸~

Related Posts: PHP UTF-8下的GBK编码检测与转换 进化史 完结篇 :

avatar