BeWithYou

胡搞的技术博客

  1. 首页
  2. PHP
  3. PHP中使用正则解决中文json解析的问题

PHP中使用正则解决中文json解析的问题


我们在使用PHP处理json字符串解析的时候,经常会碰到字段中存在中文的问题。有的时候,json字符串的提供方会对中文字段做urlencode处理,这样我们在PHP中解析就会比较方便。但是在我们调用第三方的接口的时候,对于字符串的格式是否采用urlencode是不可控的,毕竟不是自己写接口自己调用嘛。

PS: PHP中对于有中文字段的array做json_encode,默认做了urlencode操作。如果不想强制转换的话,从PHP5.4开始支持了JSON_UNESCAPED_UNICODE选项。即这么生成json字符串:

$resultArr = array ('ret_code' => 0, 'ret_msg' => '操作成功!');
$resultStr = json_encode($resultArr, JSON_UNESCAPED_UNICODE);
echo $resultStr; // print {"ret_code":0,"ret_msg":"操作成功!"}

这样就会出现,提供方的返回信息里包含了没有特别处理的中文字段。此时,直接使用json_decode方法就不行了。之前在做出票商对接的时候就遇到了这个问题。下面记录一下两个例子,和当时的处理方法。

1.调用百度的接口查询ip地址归属地

百度的坑爹接口返回的就是传说中带中文字段的json字符串,如下

{"status":"0","t":"","set_cache_time":"","data":[{"location":"广东省深圳市 电信", "titlecont":"IP地址查询", "origip":"113.110.182.82", "origipquery":"113.110.182.82", "showlamp":"1", "showLikeShare":1, "shareImage":1, "ExtendedLocation":"", "OriginQuery":"113.110.182.82", "tplt":"ip", "resourceid":"6006", "fetchkey":"113.110.182.82", "appinfo":"", "role_id":0, "disp_type":0}]}

那到该怎么办呢?我最直接的想法是,把中文字段提取出来,先用其他编码方式重新编码再塞回去,之后再解析。这样至少能保证json_decode可以正常跑起来。然后再用之前编码的方法,反编码回来即可。

想到这里,自然就得用上强大的正则表达式了。

function get_ip_data($ip, $source = 'baidu')
{
    if ($source == "baidu") {
        $ipStr = file_get_contents("http://opendata.baidu.com/api.php?query={$ip}&co=&resource_id=6006&ie=utf8&oe=gbk&format=json&tn=baidu");
        $ipStr = preg_replace_callback("/:\"(.*)\",/U",
            function($m){return ":\"".base64_encode($m[1])."\",";}, $ipStr);
        $ipArr = json_decode($ipStr, true);
        if (!empty($ipArr['data'][0])) {
            foreach ($ipArr['data'][0] as $k => $v) {
                if ($k != 'ip') {
                    $ipArr['data'][0][$k] = base64_decode($v);
                }
            }
        } else {
            return false;
        }
        return iconv('GBK', 'UTF-8', $ipArr['data'][0]['location']);
    }

}

这里我们用了php里正则替换回调的方法,将key=>value中的value提取出来,做base64编码,然后再替换回去。之后就可以正常解析了!

2.出票商返回的json格式字符串解析

这个例子里,我们把提取范围缩小一点,只有部分字符串是中文的,所以不用每个字段都抽出来。

$res = preg_replace("/\"guestteam\":\"(.*)\",\"hometeam\":\"(.*)\",/Ue","'\"guestteam\":\"'.base64_encode(\\1).'\",'.'\"hometeam\":\"'. base64_encode(\\2).'\",'",$res);

只有主队和客队对应的字段需要重新编码。这里用的是preg_replace方法,新的PHP里已经归为DEPRECATED了。所以还是用前面说的preg_replace_callback比较爽。差别大改就是把正则里的e参数,改成了回调函数的方式。

回到顶部