PHP DOMDocument loadHTML没有正确编码UTF-8

Sli*_* A. 174 php utf-8 character-encoding

我正在尝试使用DOMDocument解析一些HTML,但是当我这样做时,我突然失去了编码(至少这对我来说是这样).

$profile = "<div><p>various japanese characters</p></div>";
$dom = new DOMDocument();
$dom->loadHTML($profile); 

$divs = $dom->getElementsByTagName('div');

foreach ($divs as $div) {
    echo $dom->saveHTML($div);
}
Run Code Online (Sandbox Code Playgroud)

这段代码的结果是我得到了一堆不是日语的字符.但是,如果我这样做:

echo $profile;
Run Code Online (Sandbox Code Playgroud)

它显示正确.我尝试过saveHTML和saveXML,但都没有正确显示.我使用的是PHP 5.3.

我所看到的:

ã¤ãªãã¤å·ã·ã«ã´ã«ã¦ãã¢ã¤ã«ã©ã³ãç³»ã®å®¶åº­ã«ã9人åå¼ã®5çªç®ã¨ãã¦çã¾ãããå½¼ãå«ãã¦4人ã俳åªã«ãªã£ããç¶è¦ªã¯æ¨æã®ã»ã¼ã«ã¹ãã³ã§ãæ¯è¦ªã¯éµä¾¿å±ã®å®¢å®¤ä¿ã ã£ãã髿 ¡æä»£ã¯ã­ã£ãã£ã®ã¢ã«ãã¤ãã«å¤ãã¿ãæè²è³éãåããªããã«ããªãã¯ç³»ã®é«æ ¡ã¸é²å­¦ã
Run Code Online (Sandbox Code Playgroud)

应该展示什么:

???????????????????????9????5???????????????4???????????????????????????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

编辑:我已将代码简化为五行,以便您自己测试.

$profile = "<div lang=ja><p>???????????????????????</p></div>";
$dom = new DOMDocument();
$dom->loadHTML($profile);
echo $dom->saveHTML();
echo $profile;
Run Code Online (Sandbox Code Playgroud)

这是返回的html:

<div lang="ja"><p>イリノイ州シカゴã«ã¦ã€ã‚¢ã‚¤ãƒ«ãƒ©ãƒ³ãƒ‰ç³»ã®å®¶åº­ã«ã€</p></div>
<div lang="ja"><p>???????????????????????</p></div>
Run Code Online (Sandbox Code Playgroud)

cmb*_*ley 460

DOMDocument::loadHTML除非你另有说明,否则会将你的字符串视为ISO-8859-1.这导致UTF-8字符串被错误地解释.

如果您的字符串不包含XML编码声明,则可以添加一个以使字符串被视为UTF-8:

$profile = '<p>???????????????????????9</p>';
$dom = new DOMDocument();
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $profile);
echo $dom->saveHTML();
Run Code Online (Sandbox Code Playgroud)

如果你不知道字符串是否已经包含这样的声明,那么SmartDOMDocument中有一个解决方法可以帮助你:

$profile = '<p>???????????????????????9</p>';
$dom = new DOMDocument();
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));
echo $dom->saveHTML();
Run Code Online (Sandbox Code Playgroud)

这不是一个很好的解决方法,但由于并非所有角色都可以用ISO-8859-1(如这些武士刀)来表示,因此它是最安全的选择.

  • 仍然在2017年这个答案是相关的,也为我工作.我有我的数据库,多字节,HTML meta标签和DOM编码都设置为utf8,仍然有坏的编码从一个DOC进口节点到另一个节点.http://php.net/manual/en/function.mb-convert-encoding.php是修复. (10认同)
  • `$ dom-> loadHTML('<?xml encoding ="utf-8"?>'.$ content);`为我做了! (6认同)
  • mb_convert_encoding调用对我有用,而前置编码声明却没有.可能是因为该文件已经有一个相互矛盾的声明.非常感谢 - 为我节省了很多时间. (4认同)
  • `mb_convert_encoding`效果很好,谢谢. (4认同)
  • `$ dom-> loadHTML(mb_convert_encoding($ profile,'HTML-ENTITIES','UTF-8'));`效果很好!谢谢, (3认同)
  • 是的,它做到了。感谢您的帮助。我试过saveHTML,saveXML,没想到问题可能是在加载过程中出现的。 (2认同)
  • `$dom-&gt;loadHTML('&lt;?xml encoding="utf-8" ?&gt;' . $content);` 在 PHP7 中为我修复了它(所以它仍然是一个问题) - 这是一个非常烦人的问题,因为我在 HTML 文档中定义了 utf8(使用 `&lt;meta charset="UTF-8" /&gt;`)但是没有效果,它似乎需要 &lt;?xml 部分,这完全不直观。 (2认同)

Gre*_*eso 56

这个问题是saveHTML()saveXML(),他们都不会在Unix的正常工作.在Unix中使用时,它们不能正确保存UTF-8字符,但它们可以在Windows中使用.

解决方法非常简单:

如果您尝试默认,您将收到您描述的错误

$str = $dom->saveHTML(); // saves incorrectly
Run Code Online (Sandbox Code Playgroud)

您所要做的就是保存如下:

$str = $dom->saveHTML($dom->documentElement); // saves correctly
Run Code Online (Sandbox Code Playgroud)

这行代码将正确保存您的UTF-8字符.如果您使用,请使用相同的解决方法saveXML().


更新

正如下面评论部分中" Jack M " 所建议,并经" Pamela "和" MarcoAurélioDeleu "验证,以下变体可能适用于您的情况:

$str = utf8_decode($dom->saveHTML($dom->documentElement));
Run Code Online (Sandbox Code Playgroud)

注意

  1. 使用saveHTML()不带参数时,英文字符不会导致任何问题(因为英文字符在UTF-8中保存为单字节字符)

  2. 当你有多字节字符(如中文,俄文,阿拉伯文,希伯来文等)时会出现问题.

我建议阅读这篇文章:http://coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/.您将了解UTF-8的工作原理以及您遇到此问题的原因.这将需要大约30分钟,但它是花费的时间.

  • 这必须成为utf8_decode($ dom-> saveHTML(dom-> documentElement))来保存我的特殊字符.否则,他们只是成了别的东西.只是提到它,以防它帮助其他人. (9认同)
  • 使用此解决方案时,我不得不使用utf8_decode.谢谢! (5认同)
  • 谢谢@MrJack.我还必须做同样的事情,让它显示没有奇怪的字符`$ str = utf8_decode($ dom-> saveHTML($ dom-> documentElement));` (4认同)
  • `utf8_decode($dom-&gt;saveHTML($dom-&gt;documentElement));` 对我来说非常完美。 (3认同)
  • 你救了我的命.我无处不在寻找这个答案!谢谢! (2认同)

Hos*_*ein 14

确保将真实源文件保存为UTF-8(您甚至可能希望尝试使用UTF-8的非推荐BOM表来确保).

同样在HTML的情况下,请确保使用meta标记声明了正确的编码:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
Run Code Online (Sandbox Code Playgroud)

如果它是CMS(因为您使用Joomla标记了您的问题),您可能需要为编码配置适当的设置.

  • 作为旁注,较新的 `&lt;meta charset="UTF-8"&gt;`​​ 标签不适用于 DOMDocument。 (3认同)
  • 您的meta阻止saveHTML将ASCII以上的所有内容编码为实体.我正在寻找的解决方案:) (2认同)

Jan*_*roň 13

使用正确的 UTF-8 标头

\n

不要满足于“它有效”。

\n

@cmbuckley 在他接受的答案中建议设置<?xml encoding="utf-8" ?>为该文档。然而在 HTML 文档中使用 XML 声明有点奇怪。HTML 不是 XML(除非它是 XHTML),它可能会在到达客户端的过程中混淆浏览器和其他软件(可能是其他人报告的故障的根源)。

\n

我成功地使用了 HTML5 声明:

\n
$profile = '<p>\xe3\x82\xa4\xe3\x83\xaa\xe3\x83\x8e\xe3\x82\xa4\xe5\xb7\x9e\xe3\x82\xb7\xe3\x82\xab\xe3\x82\xb4\xe3\x81\xab\xe3\x81\xa6\xe3\x80\x81\xe3\x82\xa2\xe3\x82\xa4\xe3\x83\xab\xe3\x83\xa9\xe3\x83\xb3\xe3\x83\x89\xe7\xb3\xbb\xe3\x81\xae\xe5\xae\xb6\xe5\xba\xad\xe3\x81\xab\xe3\x80\x819</p>';\n$dom = new DOMDocument();\n$dom->loadHTML('<!DOCTYPE html><meta charset="UTF-8">' . $profile);\necho $dom->saveHTML();\n
Run Code Online (Sandbox Code Playgroud)\n

如果您使用其他标准,请使用正确的标头,DOMDocument 相当迂腐地遵循标准,并且似乎也支持 HTML5(如果不是您的情况,请尝试更新 libxml 扩展)。

\n

  • 不幸的是,PHP 不支持 HTML5,因为 libxml 不支持它。使用 `&lt;!DOCTYPE alsfjaswrtoiufn&gt;` 会得到相同的结果,即它只会输出您输入的任何内容。 (2认同)

Iva*_*van 10

您可以为行强制utf-8编码添加前缀,如下所示:

@$doc->loadHTML('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $profile);
Run Code Online (Sandbox Code Playgroud)

然后,您可以继续使用已有的代码,例如:

$doc->saveXML()
Run Code Online (Sandbox Code Playgroud)


小智 7

这花了我一段时间才能弄清楚,但这是我的答案。

在使用DomDocument之前,我将使用file_get_contents检索URL,然后使用字符串函数对其进行处理。也许不是最好的方法,但是很快。在确信Dom很快之后,我首先尝试了以下方法:

$dom = new DomDocument('1.0', 'UTF-8');
if ($dom->loadHTMLFile($url) == false) { // read the url
    // error message
}
else {
    // process
}
Run Code Online (Sandbox Code Playgroud)

尽管有适当的元标记,php设置以及此处和其他地方提供的所有其他补救措施,但在保留UTF-8编码方面却失败了。这是有效的方法:

$dom = new DomDocument('1.0', 'UTF-8');
$str = file_get_contents($url);
if ($dom->loadHTML(mb_convert_encoding($str, 'HTML-ENTITIES', 'UTF-8')) == false) {
}
Run Code Online (Sandbox Code Playgroud)

等。现在,世界一切正常。希望这可以帮助。


Ale*_*rov 6

使用它以获得正确的结果

$dom = new DOMDocument();
$dom->loadHTML('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . $profile);
echo $dom->saveHTML();
echo $profile;
Run Code Online (Sandbox Code Playgroud)

这个操作

mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8');
Run Code Online (Sandbox Code Playgroud)

这是不好的方法,因为特殊符号如 < ; ,> 可以在$profile中,并且在mb_convert_encoding之后它们不会转换两次。这是 XSS 和错误 HTML 的漏洞。


小智 5

您必须使用有意义的标头为DOMDocument提供HTML版本.就像HTML5一样.

$profile ='<?xml version="1.0" encoding="'.$_encoding.'"?>'. $html;
Run Code Online (Sandbox Code Playgroud)

也许保持你的html尽可能有效是一个好主意,所以当你开始查询时,你不会遇到问题......周围:-)并远离htmlentities!!!! 这是一个必要的来回浪费资源.保持你的代码疯狂!!!!