PHP DOMDocument loadHTML不能正确编码UTF-8

我试图parsing一些使用DOMDocument的HTML,但是当我这样做时,我突然失去了我的编码(至less这是我看来)。

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

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

 echo $profile; 

它显示正确。 我试过saveHTML和saveXML,并没有正确显示。 我正在使用PHP 5.3。

我所看到的:

 ã¤ãªãã¤å·ã·ã«ã´ã«ã¦ãã¢ã¤ã«ã©ã³ãç³»ã®å®¶åºã«ã9人åå¼ã®5çªç®ã¨ãã¦çã¾ãããå½¼ãå«ãã¦4人ã俳åªã«ãªã£ããç¶è¦ªã¯æ¨æã®ã»ã¼ã«ã¹ãã³ã§ãæ¯è¦ªã¯éµä¾¿å±ã®å®¢å®¤ä¿ã ã£ããé«æ ¡æ代ã¯ãã£ãã£ã®ã¢ã«ãã¤ãã«å¤ãã¿ãæè²è³éãåããªããã«ããªãã¯ç³»ã®é«æ ¡ã¸é²å¦ã 

应该显示什么:

 イリノイ州シカゴにて、アイルランド系の家庭に、9人兄弟の5番目として生まれる。彼を含めて4人が俳優になった。父親は木材のセールスマンで、母親は郵便局の客室係だった。高校時代はキャディのアルバイトに勤しみ、教育資金を受けながらカトリック系の高校へ進学 

编辑:我简化了代码到五行,所以你可以自己testing。

 $profile = "<div lang=ja><p>イリノイ州シカゴにて、アイルランド系の家庭に、</p></div>"; $dom = new DOMDocument(); $dom->loadHTML($profile); echo $dom->saveHTML(); echo $profile; 

这是返回的HTML:

 <div lang="ja"><p>イリノイ州シカゴã«ã¦ã€ã‚¢ã‚¤ãƒ«ãƒ©ãƒ³ãƒ‰ç³»ã®å®¶åºã«ã€</p></div> <div lang="ja"><p>イリノイ州シカゴにて、アイルランド系の家庭に、</p></div> 

除非另有说明,否则DOMDocument::loadHTML会将您的string视为在ISO-8859-1中。 这导致UTF-8string被错误地解释。 SmartDOMDocument中有一个解决方法可以帮助你:

 $profile = '<div><p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p></div>'; $dom = new DOMDocument(); $dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8')); echo $dom->saveHTML($dom->getElementsByTagName('div')->item(0)); 

另一种方法是用XML编码声明预先将HTML视为UTF-8,前提是文档不包含一个:

 $dom->loadHTML('<?xml encoding="utf-8" ?>' . $profile); 

问题在于saveHTML()saveXML() ,它们在Unix中都不能正常工作。 在Unix中使用时,它们不会正确保存UTF-8字符,但是它们在Windows中工作。

解决方法非常简单:

如果你尝试默认,你会得到你所描述的错误

 $str = $dom->saveHTML(); // saves incorrectly 

你所要做的只是保存如下:

 $str = $dom->saveHTML($dom->documentElement); // saves correctly 

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


注意

  1. 使用不带参数的saveHTML()时,英文字符不会造成任何问题(因为英文字符以UTF-8的单字节字符保存)

  2. 如果您有多字节字符(如中文,俄文,阿拉伯文,希伯来文等),则会出现问题。

我推荐阅读这篇文章: http : //coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/ 。 你将会理解UTF-8是如何工作的,以及为什么你有这个问题。 这将花费您大约30分钟,但是花费时间。

确保真正的源文件保存为UTF-8(您甚至可以尝试使用UTF-8不推荐的BOMstring来确保)。

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

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

如果它是一个CMS(正如你用Joomla标记你的问题),你可能需要configuration适当的编码设置。

你可以在强制执行utf-8编码的行utf-8前缀,如下所示:

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

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

 $doc->saveXML() 

您必须为DOMDocument提供您的HTML版本,并使用有意义的头文件。 就像HTML5一样。

 $profile ='<?xml version="1.0" encoding="'.$_encoding.'"?>'. $html; 

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

作品为我find:

 $dom = new \DOMDocument; $dom->loadHTML(utf8_decode($html)); ... return utf8_encode( $dom->saveHTML()); 

问题是,当您将参数添加到DOMDocument :: saveHTML()函数时,您将丢失编码。 在less数情况下,您需要避免使用参数,并使用旧string函数来查找您正在查找的内容。

我认为以前的答案适用于你,但由于这种解决方法对我来说不起作用,所以我添加了这个答案来帮助那些可能在我的情况下的人。

使用它来获得正确的结果

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

这个操作

 mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'); 

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

尝试使用utf8_encode