我遇到了字符编码问题.我已将其简化为以下脚本:
<!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>
Run Code Online (Sandbox Code Playgroud)
我想利用最后一个echo.但是,它删除了',为什么?
我已经尝试了所有三个选项ENT_COMPAT,ENT_QUOTES,ENT_NOQUOTES和它消除了'在所有情况下.
dec*_*eze 10
问题是’解码为Unicode字符U + 0092,UTF-8 C2 92,称为PRIVATE USE TWO:
$ php test.php | xxd
0000000: 5374 616e c292 73 Stan..s
Run Code Online (Sandbox Code Playgroud)
即,这不会解码为通常的撇号.
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
Run Code Online (Sandbox Code Playgroud)
引用维基百科:
无论页面的编码如何,数字引用始终引用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);
}
Run Code Online (Sandbox Code Playgroud)