html_entity_decode - 字符编码问题

4

我遇到了字符编码的问题。我已经将它简化成了以下的脚本:

<!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个回答

11
问题在于&#146;解码为Unicode字符U+0092,UTF-8编码为C2 92,也称为“私有使用两个”:
$ 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

引用自维基百科:

无论页面的编码如何,数字实体引用始终指向 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);
}

有趣,那我应该将 &#146; 转换为其他内容,然后再应用 html_entity_decode 吗? - Abs
+1. 我想补充一点,使用 &#39; 而不是 &#146; 将会给你想要的结果。 - adlawson
1
@Abs 是的,但在HTML源代码中,你应该看到 &#146; - Karolis
1
@Abs 如果每个人都遵循相同的规则并做对的话,这实际上不会成为问题。:o) - deceze
@deceze - 那是我们只能梦想的事情! :) - Abs
显示剩余6条评论

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