我遇到字符编码问题。我已将其简化为以下脚本:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<?php
$string = 'Stan’s';
echo $string.'<br><br>'; // Stan's
echo html_entity_decode($string).'<br><br>'; // Stan's
echo html_entity_decode($string, ENT_QUOTES, 'UTF-8'); // Stans
?>
</body>
</html>
我想使用上一个echo
。但是,它删除了'
,为什么?
我已经尝试了所有三个选项ENT_COMPAT
,ENT_QUOTES
,ENT_NOQUOTES
,并且在所有情况下都删除了'
。
答案 0 :(得分:10)
问题是’
解码为Unicode字符U + 0092,UTF-8 C2 92
,称为PRIVATE USE TWO:
$ php test.php | xxd
0000000: 5374 616e c292 73 Stan..s
即,这不会解码为通常的撇号。
html_entity_decode($string)
有效,因为它实际上并不解码实体,因为默认目标字符集是latin-1,它不能代表此字符。如果将UTF-8指定为目标字符集,则实际将对实体进行解码。
该实体的目标是Windows-1252字符集:
echo iconv('cp1252', 'UTF-8', html_entity_decode('Stan’s', ENT_QUOTES, 'cp1252'));
Stan’s
引用Wikipedia:
无论页面的编码如何,数字引用始终引用Unicode代码点。除了换行符,制表符和回车符之外,禁止使用引用永久未定义字符和控制字符的数字引用。也就是说,十六进制范围中的字符00-08,0B-0C,0E-1F,7F和80-9F不能在HTML文档中使用,甚至不能通过引用使用,因此
™
例如是不允许。但是,为了向后兼容忽略此限制的早期HTML作者和浏览器,某些浏览器将80-9F范围内的原始字符和数字字符引用解释为表示映射到Windows-1252编码中字节80-9F的字符。
所以你在这里处理遗留的HTML实体,PHP显然不像“某些”浏览器那样处理。您可能想要检查解码的实体是否在上面指定的范围内,您在Windows-1252中转码/重新编码它们,然后将它们转换为UTF-8。或要求您的用户传递有效的HTML。
此函数应处理旧版和常规HTML实体:
function legacy_html_entity_decode($str, $quotes = ENT_QUOTES, $charset = 'UTF-8') {
return preg_replace_callback('/&#(\d+);/', function ($m) use ($quotes, $charset) {
if (0x80 <= $m[1] && $m[1] <= 0x9F) {
return iconv('cp1252', $charset, html_entity_decode($m[0], $quotes, 'cp1252'));
}
return html_entity_decode($m[0], $quotes, $charset);
}, $str);
}