在项目中,因为字符编码的问题,踩了不少坑,之前踩,现在还接着踩,现在把它们总结出来,只希望以后不要再踩这坑了,我把我踩过的坑总结一下:
1.将数组转成json数据,json数据为null或为空字符串;
2.将数组转成json数据,json中的汉字乱码;
3.当json数据嵌套时(数组是个json,数组中的某个字段,也是个json字符串),json_decode失败;
1 基本概念
Unicode:(统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。
UTF-8:是一种针对Unicode的可变长度字符编码,又称万国码,UTF-8用1到4个字节编码Unicode字符。
GBK:汉字编码字符集。
json_encode:PHP中将数组转成json数据,只支持utf8格式的数据;
json_decode:PHP中将json数据转换成数组,转换后的数组是utf8格式;
2 UTF-8/GBK与json_encode
情况1:GBK–>UTF-8–>json_encode
代码如下:
$strTest = '测试用例';
$strConvet = iconv('GBK', 'UTF-8', $strTest);
var_dump($strConvet);
$strJson = json_encode($strConvet);
var_dump($strJson);
这里需要分两种情况讨论,当数据$strTest为gbk格式,输出结果:
string(12) "测试用例"
string(26) ""\u6d4b\u8bd5\u7528\u4f8b""
这里没有问题,数据是从gbk–>utf8–>json_encode,流程完全正确(说明一下,”\u6d4b\u8bd5\u7528\u4f8b”是unicode格式,也就是php进行json_encode时,会自动将utf8格式的汉字转为unicode格式,除汉字以外,其它的还是按照utf8格式输出,专门将汉字转为unicode,这样其实有个很大的好处,后面会讲)。
如果数据$strTest为utf8格式,输出结果:
string(18) "娴嬭瘯鐢ㄤ緥"
string(38) ""\u5a34\u5b2d\u762f\u9422\u3124\u7de5""
出现乱码了!!!数据是从utf8->utf8–>json_encode,也就是数据$strTest为utf8格式,然后把按照gbk的方式强转为utf8,就出现乱码了,虽然是乱码,但这些乱码仍然是utf8格式,所以json_encode后,仍然会有输出结果。
情况2:UTF-8–>GBK–>json_encode
代码如下:
$strTest = '测试用例';
$strConvet = iconv('UTF-8', 'GBK', $strTest);
var_dump($strConvet);
$strJson = json_encode($strConvet);
var_dump($strJson);
同上,这里也需要分两种情况讨论,当数据$strTest为utf8格式,输出结果:
string(8) "测试用例"
string(4) "null"
没有出现乱码,但是json_encode后的数据为null!!!数据是从utf8–>gbk–>json_encode,因为json_encode只接受utf8格式的数据,gbk的数据不能直接转成json,只会输出null。
如果数据$strTest为utf8格式,输出结果:
string(0) ""
string(2) """"
啥都没有了!!!数据是从gbk->gbk–>json_encode,也就是将utf8格式的数据,按照utf8的方式强转为gbk,直接转成空字符串。然后空的字符串json_encode后还是空字符串。(可能你会说,这个空字符串是gbk格式的啊,按照上面说的,json_encode后应该是null,我试了一下,英文字符是不区分utf8个gbk格式的,不管怎么转,输出的还是英文字符)
通过上面的2种情况,准确来说是4种情况,就基本知道为什么转码后,会出现各种各样的问题,总结一下,其实就是没有按照标准去使用转码函数。
3 UTF-8/GBK与json_decode
其实核心的东西,上面都已经讲了,只是json_decode的入参也必须是utf8格式的,只要保证这一点,json_decode后的数据就不会存在问题,但是在项目中,经过多次转码后,就不能保证json_decode的入参是utf8格式了,很容易出现乱码,我举个项目中遇到的例子。
项目示例:调用第三方的接口,会吐给我一个json串,这个json串是个数组,然后数组中有一个字段也是个json串,更恐怖的时,这个json串中的汉字居然不是unicode格式,而是个纯粹的utf8格式,示例数据如下:
{"ret":0,"msg":"OK","content":{"user_name":"","true_name":"{\"materialType\":\"测试数据类型\",\"materialFormat\":\"image\"}"}}
json_decode后的数据:
{
"ret": 0,
"msg": "OK",
"content": {
"user_name": "",
"true_name": "{\"materialType\":\"测试数据类型\",\"materialFormat\":\"image\"}"
}
}
因为项目中,我们这边处理数据的格式是GBK,所以第三方的数据会马上转成GBK格式,但是后续如果需要用到字段true_name中的数据materialType,需要将true_name单独json_decode,因为之前我们已经将其转为gbk格式,所以这里使用json_decode肯定会出现问题(这就是为什么json数据中的汉字,一般会转为unicode,如果汉字为unicode,就不会出现上述问题了)。为了解决这个问题,我们是先将第三方给我们的json数据,先转成数组,然后单独对字段true_name进行json_decode,这样所有字段就不存在json数据了,再统一将所有数据转成GBK格式。
最后补充一点,json_encode方法会自动将汉字转成unicode格式,如果因为一些特殊要求,需要将将里面的汉字保留为utf8格式(一般不要这样,会给自己埋坑,我指的是因为特殊要求,不得已才这么处理),使用方法如下:
json_encode($arrInput, 'GBK', 'UTF-8'), JSON_UNESCAPED_UNICODE)
但是这里又有个坑,看一下php帮助文档:
这个参数JSON_UNESCAPED_UNICODE,只有php5.4.0以上的版本(包括5.4.0)才能使用,如果你线下机器是php5.4.0,线上机器是php5.4.0一下版本,这个坑估计会被踩的很冤!
总结:这些坑是我一路踩过来的,估计以后还会有坑,不过我想应该不会很多了,如果还会踩到其它坑,会在该片博文中补充。