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

标签 php utf-8 domdocument

网络服务器正在使用 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,我需要更加具体吗?

最佳答案

DOMDocument::loadHTML() 需要一个 HTML 字符串。

HTML 使用 ISO-8859-1编码(ISO 拉丁字母 1 号)作为其规范的默认值。那是因为更长,见6.1. The HTML Document Character Set .实际上,这更多是对 Windows-1252 的默认支持。在常见的网络浏览器中。

我回溯到那么远是因为 PHP 的 DOMDocument 基于 libxml 并带来了 HTMLparser专为 HTML 4.0 设计的。

我会说可以安全地假设您可以加载 ISO-8859-1编码字符串。

你的字符串是 UTF-8编码。将所有高于 127/h7F 的字符转为 HTML Entities你很好。如果你不想自己做,那就是 mb_convert_encodingHTML-ENTITIES目标编码:

  • 那些具有命名实体的角色,将获得命名实体。 € -> &euro;
  • 其他人得到他们的数字(十进制)实体,例如☆ -> &#9734;

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

$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;

无论如何,这只是为了更深入地了解您的字符串。您希望将其转换为编码 loadHTML可以处理。这可以通过转换 US-ASCII 之外的所有内容来完成。进入 HTML 实体:

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

请注意您的输入实际上是 UTF-8 编码的。如果您甚至有混合编码(某些输入可能会发生这种情况)mb_convert_encoding每个字符串只能处理一种编码。我已经在上面概述了如何在正则表达式的帮助下更具体地进行字符串替换,所以我现在留下更多细节。

另一种选择是提示编码。这可以通过修改文档并添加

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

这是一个指定字符集的 Content-Type。对于无法通过网络服务器获得的 HTML 字符串(例如,保存在磁盘上或在您的示例中的字符串中),这也是最佳实践。网络服务器通常将其设置为响应 header 。

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

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

根据 HTML 2.0 规范,只能出现在 <head> 中的元素文档的部分,将自动放置在那里。这也是这里发生的事情。输出( pretty-print ):

<!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>

关于PHP DOMDocument 无法处理 utf-8 字符 (☆),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11309194/

相关文章:

java - 如何验证 UTF-8 字符串是否包含错误编码的字符

python - UTF-8 中的解码如何知道字节边界?

php - 使用 getElementById 从 <select> 获取所有 <option>

php - MySQL JSON 数据类型更新与删除

php - 在数据库中获取网站的访问者详细信息,但相同的 IP 地址被保存多次

php - 用PHP模拟 "Custom Quotes"

python - JSON解码错误 : Unexpected UTF-8 BOM: Display problems in bash?

Php cURL 网页抓取

javascript - 重定向页面后如何保存 HTML 文件

php - 更好的动态导航菜单