提问人:Slightly A. 提问时间:11/22/2011 最后编辑:cmbuckleySlightly A. 更新时间:11/14/2023 访问量:144768
PHP DOM文档加载HTML未正确编码UTF-8
PHP DOMDocument loadHTML not encoding UTF-8 correctly
问:
我正在尝试使用 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);
}
这段代码的结果是我得到了一堆不是日语的字符。但是,如果我这样做:
echo $profile;
它显示正确。我尝试过 saveHTML 和 saveXML,但都没有正确显示。我正在使用 PHP 5.3。
我所看到的:
ã¤ãªãã¤å·ã·ã«ã´ã«ã¦ãã¢ã¤ã«ã©ã³ãç³»ã®å®¶åºã«ã9人åå¼ã®5çªç®ã¨ãã¦çã¾ãããå½¼ãå«ãã¦4人ã俳åªã«ãªã£ããç¶è¦ªã¯æ¨æã®ã»ã¼ã«ã¹ãã³ã§ãæ¯è¦ªã¯éµä¾¿å±ã®å®¢å®¤ä¿ã ã£ããé«æ ¡æ代ã¯ãã£ãã£ã®ã¢ã«ãã¤ãã«å¤ãã¿ãæè²è³éãåããªããã«ããªãã¯ç³»ã®é«æ ¡ã¸é²å¦ã
应显示的内容:
イリノイ州シカゴにて、アイルランド系の家庭に、9人兄弟の5番目として生まれる。彼を含めて4人が俳優になった。父親は木材のセールスマンで、母親は郵便局の客室係だった。高校時代はキャディのアルバイトに勤しみ、教育資金を受けながらカトリック系の高校へ進学
编辑:我已将代码简化为五行,以便您可以自己测试。
$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>
答:
确保真正的源文件保存为 UTF-8(您甚至可能想尝试使用 UTF-8 的非推荐 BOM 字符以确保)。
此外,对于 HTML,请确保已使用标记声明了正确的编码:meta
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
如果是 CMS(因为您已经用 Joomla 标记了您的问题),您可能需要为编码配置适当的设置。
评论
<meta charset="UTF-8">
DOMDocument::loadHTML
除非您另有说明,否则将把您的字符串视为 ISO-8859-1(HTTP/1.1 默认字符集)。这会导致 UTF-8 字符串被错误地解释。
DOMDocument 使用 HTML4 分析器。如果您要加载 HTML5,您可能需要查看替代解决方案。
如果您正在处理 (X)HTML 的简单片段,则可以在前面附加 XML 编码声明或元字符集声明,以使字符串被视为 UTF-8:
$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
// This version preserves the original characters
$contentType = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">';
$dom->loadHTML($contentType . $profile);
echo $dom->saveHTML();
// This version will HTML-encode high-ASCII bytes
$dom->loadHTML('<meta charset="utf8">' . $profile);
echo $dom->saveHTML();
// This version will also HTML-encode high-ASCII bytes,
// and won't work for LIBXML_DOTTED_VERSION >= 2.12.0
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $profile);
echo $dom->saveHTML();
如果您不知道 HTML 是否已经包含声明,SmartDOMDocument 中有一个解决方法可以帮助您:
$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));
echo $dom->saveHTML();
在 PHP 8.2+ 中,您会收到弃用警告,因此替代方案是:
$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML(mb_encode_numericentity($profile, [0x80, 0x10FFFF, 0, ~0], 'UTF-8'));
echo $dom->saveHTML();
(有关这个相当神秘的数组的更好解释,请参阅此处。
这不是一个很好的解决方法,但由于并非所有字符都可以在 ISO-8859-1 中表示(例如这些武士刀),因此它是最安全的替代方案。
评论
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $content);
在 PHP7 中为我修复了它(所以它仍然是一个问题) - 这是一个非常烦人的问题,因为我在 HTML 文档中定义了 utf8(使用 )但这没有效果,它似乎需要 <?xml 部分,这完全不直观。<meta charset="UTF-8" />
问题是,当您向函数添加参数时,会丢失编码。在少数情况下,您需要避免使用该参数并使用旧的字符串函数来查找您要查找的内容。DOMDocument::saveHTML()
我认为前面的答案对你有用,但由于这种解决方法对我不起作用,我添加该答案是为了帮助可能与我一样的人。
必须向 DOMDocument 提供具有有意义的标头的 HTML 版本。 就像 HTML5 一样。
$profile ='<?xml version="1.0" encoding="'.$_encoding.'"?>'. $html;
也许保持你的html尽可能有效是一个好主意,这样你就不会在开始查询时遇到问题......周围:-)并远离!!!这是一个必要的来回浪费资源。
让你的代码保持疯狂!!!htmlentities
评论
问题在于 和 ,它们都无法在 Unix 中正常工作。在 Unix 中使用时,它们不能正确保存 UTF-8 字符,但它们可以在 Windows 中工作。saveHTML()
saveXML()
解决方法非常简单:
如果您尝试默认设置,您将收到您描述的错误
$str = $dom->saveHTML(); // saves incorrectly
您所要做的就是保存如下:
$str = $dom->saveHTML($dom->documentElement); // saves correctly
这行代码将使您的 UTF-8 字符正确保存。如果使用 ,请使用相同的解决方法。saveXML()
更新
正如“Jack M”在下面的评论部分所建议的那样,并由“Pamela”和“Marco Aurélio Deleu”验证,以下变体可能适用于您的情况:
$str = utf8_decode($dom->saveHTML($dom->documentElement));
更新 2
utf8_decode
现已弃用。另一种选择是 .您必须根据需要进行设置。mb_convert_encoding()
注意
不带参数使用时,英文字符不会造成任何问题(因为英文字符在 UTF-8 中保存为单字节字符)
saveHTML()
当您有多字节字符(例如中文、俄语、阿拉伯语、希伯来语......等)
我建议阅读这篇文章: http://coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/.您将了解 UTF-8 的工作原理以及为什么会出现此问题。大约需要 30 分钟,但时间花得很值。
评论
$str = utf8_decode($dom->saveHTML($dom->documentElement));
utf8_decode($dom->saveHTML($dom->documentElement));
对我来说做得很完美。
您可以在强制编码的行前加上前缀,如下所示:utf-8
@$doc->loadHTML('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $profile);
然后,您可以继续使用已有的代码,例如:
$doc->saveXML()
使用它以获得正确的结果
$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');
这是不好的方式,因为像 < 这样的特殊符号;, > ;可以在$profile,mb_convert_encoding后不会转换两次。它是 XSS 和不正确的 HTML 的漏洞。
评论
为我找到的作品:
$dom = new \DOMDocument;
$dom->loadHTML(utf8_decode($html));
...
return utf8_encode( $dom->saveHTML());
评论
?
)
我花了一段时间才弄清楚,但这是我的答案。
在使用 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
}
尽管有适当的元标记、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) {
}
等。现在世界一切都好了。
评论
DomDocument('1.0', 'UTF-8')
Deprecated: mb_convert_encoding(): Handling HTML entities via mbstring is deprecated; use htmlspecialchars, htmlentities, or mb_encode_numericentity/mb_decode_numericentity instead
唯一对我有用的是公认的答案
$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $profile);
echo $dom->saveHTML();
然而
这带来了新的问题,即在文件的输出中。<?xml encoding="utf-8" ?>
对我来说,解决方案是做
foreach ($doc->childNodes as $xx) {
if ($xx instanceof \DOMProcessingInstruction) {
$xx->parentNode->removeChild($xx);
}
}
一些解决方案告诉我,要删除标题,我必须执行xml
$dom->saveXML($dom->documentElement);
对于部分文档(例如,带有两个标签的文档),这不适用于我,只有其中一个标签被返回。<p>
<p>
对 UTF-8 使用正确的标头
不要满足于“它有效”。
@cmbuckley在他接受的答案中建议设置文件。但是,在HTML文档中使用XML声明有点奇怪。HTML 不是 XML(除非它是 XHTML),它可能会在客户端的途中混淆浏览器和其他软件(可能是其他人报告的故障的来源)。<?xml encoding="utf-8" ?>
我成功使用了 HTML5 声明:
$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML('<!DOCTYPE html><meta charset="UTF-8">' . $profile);
echo $dom->saveHTML();
如果您使用其他标准,请使用正确的标头,DOMDocument 非常迂腐地遵循标准,并且似乎也支持 HTML5(如果不是您的情况,请尝试更新 libxml 扩展)。
评论
<!DOCTYPE alsfjaswrtoiufn>
评论