ChatGPT解决这个技术问题 Extra ChatGPT

PHP DOMDocument loadHTML 未正确编码 UTF-8

我正在尝试使用 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>
这可能会对您有所帮助。 stackoverflow.com/questions/1580543/…
谢谢。我检查了所有这些,但没有任何帮助。我没有得到????,而是其他一些奇怪的文字。我将尝试将其粘贴到此处,但不知道该站点将如何显示它。
尝试使用 utf8_encode
试过没有成功。返回与以前相同的字符。

c
cmbuckley

DOMDocument::loadHTML 会将您的字符串视为 ISO-8859-1(HTTP/1.1 默认字符集),除非您另有说明。这会导致 UTF-8 字符串被错误地解释。

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

$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $profile);
echo $dom->saveHTML();

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

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

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


是的,它做到了。谢谢您的帮助。我试过saveHTML、saveXML,没想到问题可能是在加载过程中出现的。
mb_convert_encoding 调用对我有用,而在编码声明之前没有。可能是因为该文件已经有一个冲突的声明。非常感谢 - 为我节省了很多时间来追查这个问题。
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $content); 在 PHP7 中为我修复了它(所以它仍然是一个问题) - 这是一个非常烦人的问题,因为我在 HTML 文档中定义了 utf8(使用 <meta charset="UTF-8" />)但是没有效果,它似乎需要<?xml 部分,这完全不直观。
仍然在 2017 年,这个答案是相关的,也对我有用。我将我的数据库、多字节、html 元标记和 DOM 编码都设置为 utf8 并且在将节点从一个 DOC 导入到另一个 DOC 时仍然存在错误的编码。 php.net/manual/en/function.mb-convert-encoding.php 是解决方法。
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8')); 效果很好!谢谢,
G
Greeso

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

解决方法非常简单:

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

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

笔记

不带参数使用saveHTML()时英文字符不会有任何问题(因为在UTF-8中英文字符被保存为单字节字符)当您有多字节字符时会出现问题(例如中文、俄文、阿拉伯文、希伯来文) , ...ETC。)

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


使用此解决方案时,我必须使用 utf8_decode。谢谢!
utf8_decode 是什么意思?我道歉,但我不明白你的意思。
这必须变成 utf8_decode($dom->saveHTML(dom->documentElement)) 以保留我的特殊字符。否则,他们只是变成了别的东西。只是提到它以防它帮助别人。
谢谢@MrJack。我也必须这样做才能让它显示没有奇怪的字符 $str = utf8_decode($dom->saveHTML($dom->documentElement));
utf8_decode($dom->saveHTML($dom->documentElement)); 为我做得很好。
H
Hossein

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

同样对于 HTML,请确保您已使用 meta 标记声明了正确的编码:

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

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


我明白你在说什么,但我在显示字符时没有问题。如果我做“回声 $profile;”它工作正常。当 DomDocument 得到它时,它就开始失败了。
您的元阻止 saveHTML 将 ASCII 以上的所有内容编码为实体。我正在寻找的解决方案:)
附带说明一下,较新的 <meta charset="UTF-8"> 标记不适用于 DOMDocument。
@Taylan:<meta charset="UTF-8"> 完全没有问题:见 3v4l.org/AATjh
D
Dharman

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

在使用 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) {
}

等等。现在世界上一切都很好。


只是想在上面的答案中添加另一种解决此问题的方法,在其他地方也建议使用以下方法: if ($dom->loadHTML('' . $str) = =假)。发布我的答案后,我发现我的第一个建议失败但第二个建议有效。
即使没有 DomDocument('1.0', 'UTF-8') 中的参数,也适用于我。但在我的情况下,只加载了部分 html。
非常感谢,为我处理希伯来语工作
t
trincot

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

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

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

$doc->saveXML()

J
Jan Turoň

为 UTF-8 使用正确的标头

不要满足于“它有效”。

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

我成功使用了 HTML5 声明:

$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML('<!DOCTYPE html><meta charset="UTF-8">' . $profile);
echo $dom->saveHTML();

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


不幸的是,PHP 中不支持 HTML5,因为 libxml 不支持它。使用 <!DOCTYPE alsfjaswrtoiufn> 您会得到相同的结果,即它只会输出您输入的任何内容。
我在 Windows 上运行 PHP 8.1.0 并且只添加标签 对我来说很好。不需要使用 也不需要
c
cmbuckley

您必须为 DOMDocument 提供一个带有有意义的标头的 HTML 版本。就像 HTML5 一样。

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

让您的 html 尽可能有效可能是一个好主意,这样您就不会在开始查询时遇到问题... :-) 并远离 htmlentities!!!!那是一种必要的来回浪费资源。保持你的代码疯狂!!!!


这或多或少是已接受答案的一部分....
A
Alexander Goncharov

使用它以获得正确的结果

$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 的漏洞。


您能否详细说明“在 mb_convert_encoding 之后它们不会转换两次”?
m
mMo

为我找到工作:

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

注意,utf8_decode 可能会丢失信息(替换为 ?
L
Luke Madhanga

唯一对我有用的是接受的答案

$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> 标签被返回。


x
xKobalt

问题是当您向 DOMDocument::saveHTML() 函数添加参数时,您会丢失编码。在少数情况下,您需要避免使用参数并使用旧的字符串函数来查找您要查找的内容。

我认为先前的答案对您有用,但是由于此解决方法对我不起作用,因此我添加了该答案以帮助可能与我有关的人。