PHP json编码 - 格式错误的UTF-8字符,可能是错误的编码。

70

我正在使用json_encode($data)转换数据数组,其中有一个字段包含俄语字符。

我使用了mb_detect_encoding()来显示该字段的编码格式,结果显示为UTF-8。

我认为,json编码失败是由于其中包含一些类似于"ра▒"的非法字符。我尝试对数据使用utf8_encode,它可以跳过该错误,但此时数据的外观已经不正确了。

针对这个问题应该怎么办?


7
我尝试了很多东西。- 比如什么?请展示你的代码/研究。 - Script47
1
你尝试过使用 JSON_UNESCAPED_UNICODE 选项吗? - McRed
已经尝试过"JSON_UNESCAPED_UNICODE",但不起作用。 - sparkmix
我尝试了其他方法,但要么返回相同的错误,要么字符完全改变,无法阅读。 - sparkmix
utf8_encode()函数用于将8859-1编码转换为UTF8编码,而将UTF8字符串传递给它会破坏该字符串。 - Sammitch
9个回答

110

即使大部分字符是UTF-8,如果存在一些非UTF-8字符,则会出现此问题。这将删除任何非UTF-8字符,然后它就正常工作了。

$data['name'] = mb_convert_encoding($data['name'], 'UTF-8', 'UTF-8');

7
你可能还想添加这个 $mysqli->set_charset("utf8"); - Justin Joy
我尝试通过添加以下代码来查找无效字符串: foreach ($addresses as $address) { $converted = mb_convert_encoding($address, 'UTF-8', 'UTF-8'); if ($converted !== $address) { dd($addresses); } }两点:
  1. $converted !== $address 条件从未满足。我想这是因为 === 是一个“二进制安全”的运算符...
  2. 最后我没有得到错误,即使我从未将 $converted 分配给任何东西!就像 mb_convert_encoding() 通过引用接受字符串一样,尽管它不是...
- pilat
有趣的是,由于糟糕的编码,mb_check_encoding()json_decode()两个函数都不能正常工作。而mb_detect_encoding()却可以。这是一个坏的UTF-8编码文件的问题。但是经过mb_convert_encoding()之后,一切都按预期工作了。 - Dan

48

如果您有一个多维数组需要编码为JSON格式,则可以使用以下函数:

如果出现JSON_ERROR_UTF8:

则请您进行相应的处理。

$encoded = json_encode( utf8ize( $responseForJS ) );

以下函数用于递归编码数组数据

/* Use it for json_encode some corrupt UTF-8 chars
 * useful for = malformed utf-8 characters possibly incorrectly encoded by json_encode
 */
function utf8ize( $mixed ) {
    if (is_array($mixed)) {
        foreach ($mixed as $key => $value) {
            $mixed[$key] = utf8ize($value);
        }
    } elseif (is_string($mixed)) {
        return mb_convert_encoding($mixed, "UTF-8", "UTF-8");
    }
    return $mixed;
}

9
mb_convert_encoding函数会自行进行递归工作,正如您可以在文档链接中看到的那样:_如果val是一个数组,则它所有的字符串值都将被递归转换。_因此,不需要使用utf8ize函数。您只需要使用json_encode(mb_convert_encoding($responseForJS, "UTF-8", "UTF-8"));即可。 - elnezah
6
仅当您使用 PHP 7.2 或更高版本时,mb_convert_encoding 才能转换数组,这里需要澄清。否则,此函数可以正常工作。 - mylesmg

32

谢谢,这对我有用,因为我的API响应标题字符串中有表情符号,但我有一个困惑,我在某个地方读到表情符号是UTF-8字符,那么为什么字符串中的表情符号会导致此错误的UTF-8字符格式不正确? - Haritsinh Gohil
1
@HaritsinhGohil 或许有些表情符号是有效的 UTF-8 字符,而有些则不是... - hugsbrugs

28
请确保使用字符集为 iso 的 utf8 初始化您的 Pdo 对象。这样可以解决此问题,避免任何重新 utf8 化的操作。
$pdo = new PDO("mysql:host=localhost;dbname=mybase;charset=utf8", 'user', 'password');

这解决了我的问题。它也适用于其他连接类型,比如针对MSSQL Server的dlib。 - Alexandru Topală
被交给一个旧项目来解决编码问题,这帮助了我很多。唯一的区别是该项目使用了 ADO 并且解决方案有点不同,通过使用 setCharset() 方法解决了这个问题,更多信息请参见 http://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:setcharset。 - yurguis

9

您只需要在PDO连接中添加charset=utf8,像下面的PDO连接行一样:

$pdo = new PDO("mysql:host=localhost;dbname=mybase;charset=utf8", 'user', 'password');

希望这能帮到您。

3
在JSON编码之前,需要删除HTML实体。我在PHP中使用了html_entity_decode(),问题得到了解决。
$json = html_entity_decode($source);
$data = json_decode($json,true);

0
如果您的数据在数据库中已经被良好编码,那么在使用 json_encode 之前,请确保使用 mb_* 函数进行字符串处理。像 substr 或 strlen 这样的函数在 utf8mb4 下无法正常工作,可能会截断您的文本并留下一个格式不正确的 UTF8。

0

你的结果集中是否有UUID?如果是这样,以下数据库标志将会有所帮助:

PDO::DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER => true

-1

我知道这个话题有点老了,但对我来说正是我需要的。我只需要修改答案中的“jayashan perera”。

//...code
        $stmt->execute();
        $result = $stmt->fetchAll(PDO::FETCH_ASSOC);


        for ($i=0; $i < sizeof($result) ; $i++) { 
            $tempCnpj = $result[$i]['CNPJ'];
            $tempFornecedor = json_encode(html_entity_decode($result[$i]['Nome_fornecedor']),true) ;
            $tempData = $result[$i]['efetivado_data'];
            $tempNota = $result[$i]['valor_nota'];
            $arrResposta[$i] = ["Status"=>"true", "Cnpj"=>"$tempCnpj", "Fornecedor"=>$tempFornecedor, "Data"=>"$tempData", "Nota"=>"$tempNota" ];
        }

        echo json_encode($arrResposta);

而且没有使用 .js

obj = JSON.parse(msg); 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接