html_entity_decode - 字符编码问题

时间:2011-08-21 11:30:40

标签: php

我遇到字符编码问题。我已将其简化为以下脚本:

<!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&#146;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_COMPATENT_QUOTESENT_NOQUOTES,并且在所有情况下都删除了'

1 个答案:

答案 0 :(得分:10)

问题是&#146;解码为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&#146;s', ENT_QUOTES, 'cp1252'));

Stan’s

引用Wikipedia

  

无论页面的编码如何,数字引用始终引用Unicode代码点。除了换行符,制表符和回车符之外,禁止使用引用永久未定义字符和控制字符的数字引用。也就是说,十六进制范围中的字符00-08,0B-0C,0E-1F,7F和80-9F不能在HTML文档中使用,甚至不能通过引用使用,因此&#153;例如是不允许。但是,为了向后兼容忽略此限制的早期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);
}