Php 如何防止json_encode()删除包含无效字符的字符串

Php 如何防止json_encode()删除包含无效字符的字符串,php,utf-8,json,Php,Utf 8,Json,对于包含无效(非UTF-8)字符的字符串,是否有方法防止json_encode()返回null 在一个复杂的系统中调试可能是一件麻烦事。更适合实际查看无效字符,或者至少忽略它。目前,json\u encode()将自动删除整个字符串 示例(在UTF-8中): 导致 [null,"Washington","Nairobi"] 预期结果: ["D�sseldorf","Washington","Nairobi"] 注意:我不想在json_encode()中使用断字符串。我正在寻找更容易诊断编码错

对于包含无效(非UTF-8)字符的字符串,是否有方法防止
json_encode()
返回
null

在一个复杂的系统中调试可能是一件麻烦事。更适合实际查看无效字符,或者至少忽略它。目前,
json\u encode()
将自动删除整个字符串

示例(在UTF-8中):

导致

[null,"Washington","Nairobi"]
预期结果:

["D�sseldorf","Washington","Nairobi"]

注意:我不想在json_encode()中使用断字符串。我正在寻找更容易诊断编码错误的方法。
null
字符串对此没有帮助。

您需要知道正在处理的所有字符串的编码,否则您将进入一个痛苦的世界

UTF-8是一种易于使用的编码。此外,JSON被定义为使用UTF-8(http://www.json.org/JSONRequest.html). 那么为什么不使用它呢


简短回答:避免json_encode()丢弃字符串的方法是确保它们是有效的UTF-8。

php确实尝试抛出错误,但只有在关闭显示错误时才这样做。这很奇怪,因为
display\u errors
设置仅用于控制是否将错误打印到标准输出,而不是是否触发错误。我想强调的是,当你有
display_errors
on时,尽管你可能会看到其他各种各样的php错误,但php不仅仅隐藏这个错误,它甚至不会触发它。这意味着它不会出现在任何错误日志中,也不会调用任何自定义错误处理程序。错误永远不会发生

下面的一些代码演示了这一点:

error_reporting(-1);//report all errors
$invalid_utf8_char = chr(193);

ini_set('display_errors', 1);//display errors to standard output
var_dump(json_encode($invalid_utf8_char));
var_dump(error_get_last());//nothing

ini_set('display_errors', 0);//do not display errors to standard output
var_dump(json_encode($invalid_utf8_char));
var_dump(error_get_last());// json_encode(): Invalid UTF-8 sequence in argument
这种奇怪和不幸的行为与这个bug和其他一些bug有关,看起来它永远不会被修复

解决方法:

在将字符串传递给json_encode之前清理字符串可能是一个可行的解决方案

$stripped_of_invalid_utf8_chars_string = iconv('UTF-8', 'UTF-8//IGNORE', $orig_string);
if ($stripped_of_invalid_utf8_chars_string !== $orig_string) {
    // one or more chars were invalid, and so they were stripped out.
    // if you need to know where in the string the first stripped character was, 
    // then see http://stackoverflow.com/questions/7475437/find-first-character-that-is-different-between-two-strings
}
$json = json_encode($stripped_of_invalid_utf8_chars_string);

手册上说

//忽略
以静默方式丢弃目标中非法的字符 查塞特

因此,从理论上讲,通过首先删除有问题的字符,json_encode()不应该得到任何阻塞和失败的信息。我还没有验证带有
//IGNORE
标志的iconv的输出是否与json完全兼容,因为json编码的概念是什么是有效的utf8字符,所以买家要小心…因为可能存在仍然失败的边缘情况。我讨厌角色设置问题

编辑
在PHP7.2+中,
json\u encode
似乎有一些新标志:
JSON\u无效\u UTF8\u忽略
JSON\u无效\u UTF8\u替换

目前还没有太多文档,但就目前而言,此测试应该可以帮助您了解预期的行为:

在PHP7.3+中,有一个新的标志
JSON\u THROW\u ON\u ERROR
。看

这就解决了问题。 我不知道为什么php的家伙没有通过修复
json\u encode()
让生活变得更轻松

无论如何,使用上面的代码可以让json_encode()创建对象,即使数据包含特殊字符(例如瑞典字母)

然后可以在javascript中使用结果,而无需将数据解码回其原始编码(使用
escape()
unescape()
encodeURIComponent()
decoduricomponent()

我在php(smarty)中这样使用它:

然后,我将结果发送到javascript,只需
innerHTML
文档中的就绪模板(HTMLpeace)


简单地说,上面的代码应该在
json\u encode()
中实现,以允许它与任何编码一起工作。

您可以直接使用json\u encode和json\u UNESCAPED\u UNICODE选项(>=PHP5.4.0),而不是使用iconv函数

确保将“charset=utf-8”放在php文件的头中:

标题('Content-Type:application/json;charset=utf-8')


此函数将从字符串中删除所有无效的UTF8字符:

function removeInvalidChars( $text) {
    $regex = '/( [\x00-\x7F] | [\xC0-\xDF][\x80-\xBF] | [\xE0-\xEF][\x80-\xBF]{2} | [\xF0-\xF7][\x80-\xBF]{3} ) | ./x';
    return preg_replace($regex, '$1', $text);
}
我在将Excel文档转换为json后使用它,因为Excel文档不能保证使用UTF8格式


我认为没有一种特别明智的方法可以将无效字符转换为可见但有效的字符。通过改变上面的正则表达式,您可以用U+FFFD(unicode)替换无效字符,但这并不能提供比删除无效字符更好的用户体验。

要获得json失败时的信息性错误通知,我们使用以下帮助器:

  • 临时安装自定义错误处理程序,以捕获用于编码/解码的json错误
  • 发生错误时引发RuntimeException

字符串
“Düsseldorf”
是否仅在
utf8\u decode()时无效?@Matt no,这只是一个为测试应答者创建断开字符串的示例,因此您会得到一些可能包含无效UTF-8字符串的JSON数据?我只是为我的JSON解码器编写了一个包装器,首先使用mb_detect_encoding($str)检查字符串。有一个JSON_encode()在中实现-首先它不太关心字符集。但是我想来自ZendF的那本也可以很容易地改编。是的,没错,我知道这一点。正如我所说的,当JSON的某些部分突然消失(而不是看起来破碎)时,调试一个破碎的传入编码变得异常困难。这更容易发现错误,而不是绕过破损的编码本身,或者用测试每个字符串编码的东西替换json_decode(),并在任何字符串无效时在实际看到的地方抱怨UTF-8?有趣而且听起来奇怪!我明天会调查这件事。对我来说,一个警告就足够了。这个想法看起来很有趣,可能会奏效。我明天也会试试。这对我很有效。我现在正在
iconv()
整理数据
$s = iconv('UTF-8', 'UTF-8//IGNORE', $s);
$template = iconv('UTF-8', 'UTF-8//IGNORE', $screen->fetch("my_template.tpl"));
function removeInvalidChars( $text) {
    $regex = '/( [\x00-\x7F] | [\xC0-\xDF][\x80-\xBF] | [\xE0-\xEF][\x80-\xBF]{2} | [\xF0-\xF7][\x80-\xBF]{3} ) | ./x';
    return preg_replace($regex, '$1', $text);
}