utf 8-PHP DomDocument无法处理utf-8字符(☆)

网络服务器正在以utf-8编码提供响应,所有文件都以utf-8编码保存,我所知的所有设置都已设置为utf-8编码。

这是一个快速程序,用于测试输出是否有效:

<?php
$html = <<<HTML
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Test!</title>
</head>
<body>
    <h1>☆ Hello ☆ World ☆</h1>
</body>
</html>
HTML;

$dom = new DomDocument("1.0", "utf-8");
$dom->loadHTML($html);

header("Content-Type: text/html; charset=utf-8");
echo($dom->saveHTML());

该程序的输出为:

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Test!</title></head><body>
    <h1>&acirc;&#152;&#134; Hello &acirc;&#152;&#134; World &acirc;&#152;&#134;</h1>
</body></html>

呈现为:

你好-世界-


我可能做错了什么? 我必须告诉DomDocument正确处理utf-8的具体程度是多少?

Greg asked 2020-01-13T21:12:20Z
3个解决方案
112 votes

<head>需要HTML字符串。

HTML根据其规范将<head>编码(ISO拉丁字母1)作为默认设置。 那是因为更长,请参见6.1。 HTML文档字符集。 实际上,更多的是常见的Web浏览器中对US-ASCII的默认支持。

我之所以回去很远,是因为PHP的DOMDocument基于libxml,并且带来了专为HTML 4.0设计的HTMLparser。

我想可以假设您可以加载<head>编码的字符串。

您的字符串是<head>编码的。 将所有高于127 / h7F的字符转换为HTML实体,就可以了。 如果您不想自己做,那就是US-ASCIIHTML-ENTITIES目标编码的作用:

  • 具有命名实体的那些字符将获得命名实体。 <head>
  • 其他人获得其数字(十进制)实体,例如 <head>

以下是一个代码示例,该示例通过使用回调函数使进度更加明显:

$html = preg_replace_callback('/[\x{80}-\x{10FFFF}]/u', function($match) {
    list($utf8) = $match;
    $entity = mb_convert_encoding($utf8, 'HTML-ENTITIES', 'UTF-8');
    printf("%s -> %s\n", $utf8, $entity);
    return $entity;
}, $html);

您的字符串的以下示例输出:

☆ -> &#9734;
☆ -> &#9734;
☆ -> &#9734;

无论如何,那只是为了更深入地研究您的字符串。 您想要将其转换为<head>可以处理的编码。 可以通过将US-ASCII之外的所有内容转换为HTML实体来完成:

$us_ascii = mb_convert_encoding($utf_8, 'HTML-ENTITIES', 'UTF-8');

请注意,您的输入实际上是UTF-8编码的。 如果您甚至使用了混合编码(某些输入可能会发生这种情况),则<head>每个字符串只能处理一种编码。 我已经在上面概述了如何在正则表达式的帮助下更具体地进行字符串替换,因此现在我将进一步详细介绍。

另一种选择是提示编码。 根据您的情况,可以通过修改文档并添加一个

<meta http-equiv="content-type" content="text/html; charset=utf-8">

这是指定字符集的Content-Type。 对于无法通过网络服务器使用的HTML字符串(例如,保存在磁盘上或如示例中的字符串中),这也是最佳做法。 Web服务器通常将其设置为响应头。

如果您不在乎错误放置的警告,则可以将其添加到字符串的前面:

$dom = new DomDocument();
$dom->loadHTML('<meta http-equiv="content-type" content="text/html; charset=utf-8">'.$html);

根据HTML 2.0规范,仅会出现在文档的<head>部分中的元素将自动放置在此处。 这也是这里发生的情况。 输出(漂亮打印):

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta charset="utf-8">
    <title>Test!</title>
  </head>
  <body>
    <h1>☆ Hello ☆ World ☆</h1>    
  </body>
</html>
hakre answered 2020-01-13T21:13:43Z
15 votes

有一个更快的解决方案,在将html文档加载到DOMDocument中之后,您只需设置(或更确切地说说是reset)原始编码即可。 这是一个示例代码:

$dom = new DOMDocument();
$dom->loadHTML('<?xml encoding="UTF-8">' . $html);

foreach ($dom->childNodes as $item)
    if ($item->nodeType == XML_PI_NODE)
        $dom->removeChild($item);
$dom->encoding = 'UTF-8'; // reset original encoding
DeZeA answered 2020-01-13T21:14:03Z
11 votes
<?php
  header("Content-type: text/html; charset=utf-8");
  $html = <<<HTML
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Test!</title>
</head>
<body>
    <h1>☆ Hello ☆ World ☆</h1>
</body>
</html>
HTML;

  $html = mb_convert_encoding($html, 'HTML-ENTITIES', "UTF-8");
  $dom = new DomDocument("1.0", "utf-8");
  $dom->loadHTML($html);

  header("Content-Type: text/html; charset=utf-8");
  echo($dom->saveHTML());

输出:

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Test!</title></head><body>
    <h1>&#9734; Hello &#9734; World &#9734;</h1>
</body></html>
Vladimir Kadalashvili answered 2020-01-13T21:14:23Z
translate from https://stackoverflow.com:/questions/11309194/php-domdocument-failing-to-handle-utf-8-characters