PHP DOMDocument loadHTMLがUTF-8を正しくエンコードしない


194

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人が俳優になった。父親は木材のセールスマンで、母親は郵便局の客室係だった。高校時代はキャディのアルバイトに勤しみ、教育資金を受けながらカトリック系の高校へ進学

編集:自分でテストできるように、コードを5行に簡略化しました。

$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/...
frustratedtech

ありがとう。私はそれらすべてをチェックしましたが何も助けにはなりませんでした。????が表示されませんが、その他の奇妙なテキストが表示されます。ここに貼り付けますが、サイトでどのように表示されるかわかりません。
やや

使用してみてくださいはutf8_encode
Webnet

成功しなかった。以前と同じ文字を返しました。
やや

回答:


512

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

文字列にそのような宣言が既に含まれているかどうかがわからない場合は、SmartDOMDocumentに次のような回避策があります。

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

これは優れた回避策ではありませんが、すべての文字がISO-8859-1で表現できるわけではないため(これらのカタナのように)、これが最も安全な代替手段です。


1
はい、それで終わりました。ご協力ありがとうございました。saveHTML、saveXMLを試しましたが、ロード中に問題が発生している可能性があるとは思いませんでした。
やや

4
mb_convert_encoding呼び出しは私にとってはうまくいきましたが、エンコーディング宣言を前に付けるとうまくいきませんでした。その文書にはすでに矛盾する宣言があったためと思われます。多くの感謝-これを追いかけて多くの時間を節約しました。
Peter Bagnall 2013

1
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $content);PHP7でそれを修正しました(まだ問題です)-これは本当に厄介な問題です。HTMLドキュメントで(を使用して<meta charset="UTF-8" />)utf8を定義したためですが、効果がなく、<?xmlパーツが必要なようです。完全に直感的ではありません。
iquito

11
2017年もこの回答は適切であり、私にとってもうまくいきました。データベース、マルチバイト、htmlメタタグ、およびDOMエンコーディングをすべてutf8に設定しましたが、1つのDOCから別のDOCにノードをインポートする際のエンコーディングがまだ不十分でした。php.net/manual/en/function.mb-convert-encoding.phpが修正されました。
Louis Loudog Trottier 2017

6
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));よく働く!、ありがとう
VEE

66

問題はとにsaveHTML()ありsaveXML()、どちらもUnixでは正しく機能しません。Unixで使用した場合、UTF-8文字は正しく保存されませんが、Windowsでは機能します。

回避策は非常に簡単です。

デフォルトを試すと、説明したエラーが発生します

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

次のように保存するだけです。

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

このコード行により、UTF-8文字が正しく保存されます。を使用している場合は、同じ回避策を使用してくださいsaveXML()


更新

以下のコメントセクションの「ジャックM」によって提案され、「パメラ」および「マルコアウレリオデリュー」によって検証されたように、次のバリエーションがあなたのケースで機能する可能性があります。

$str = utf8_decode($dom->saveHTML($dom->documentElement));

注意

  1. saveHTML()パラメーターなしで使用する場合、英語の文字は問題を引き起こしません(英語の文字はUTF-8では1バイト文字として保存されるため)。

  2. この問題は、マルチバイト文字(中国語、ロシア語、アラビア語、ヘブライ語など)がある場合に発生します。

この記事を読むことをお勧めします:http : //coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/。UTF-8がどのように機能するか、なぜこの問題があるのか​​を理解できます。約30分かかりますが、十分に時間をかけてください。


5
このソリューションを使用している間、私はutf8_decodeを実行する必要がありました。ありがとう!
Jack M.

9
これは、特殊文字を保持するためにutf8_decode($ dom-> saveHTML(dom-> documentElement))になる必要がありました。そうでなければ、彼らはただ別のものになった。それが他の誰かを助ける場合に備えて、それを言及するだけです。
ジャックM.

4
@MrJackに感謝します。奇妙な文字なしで表示するためにも同じことをしなければなりませんでした$str = utf8_decode($dom->saveHTML($dom->documentElement));
Pamela

1
utf8_decode($dom->saveHTML($dom->documentElement));私のためにそれを完全にやった。
MarcoAurélioDeleu 2016年

2
あなたはこれで私の命を救った。私はどこでもこの答えを探しました!ありがとうございました!
Paulo Hgo 2017年

15

実際のソースファイルがUTF-8で保存されていることを確認してください(UTF-8で非推奨のBOM文字を試して確認することもできます)。

また、HTMLの場合は、metaタグを使用して正しいエンコーディングを宣言していることを確認してください。

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

CMSの場合(質問にJoomlaをタグ付けした場合)、エンコーディングに適切な設定を行う必要がある場合があります。


あなたの言っていることは理解できましたが、文字の表示に問題はありません。「echo $ profile」を実行すると、それは正常に動作します。DomDocumentがそれを手に入れると、失敗し始めます。
やや

2
メタにより、saveHTMLがASCIIを超えるすべてのものをエンティティにエンコードするのを防ぎます。私が探していた解決策:)
sod

2
<meta charset="UTF-8">ちなみに、新しいタグはDOMDocumentでは機能しません。
Taylan

10

次のutf-8ように、エンコーディングを強制する行にプレフィックスを付けることができます。

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

その後、次のような既存のコードを続行できます。

$doc->saveXML()

10

これを理解するには少し時間がかかりましたが、ここに私の答えがあります。

DomDocumentを使用する前に、file_get_contentsを使用してURLを取得し、文字列関数で処理します。おそらく最善の方法ではありませんが、迅速です。ドムが同じくらい迅速であると確信した後、私は最初に以下を試しました:

$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( '<?xml encoding = "UTF-8">'。$ str)= = false)。私の回答を投稿した後、最初の提案は失敗しましたが、2番目の提案は機能しました。
サム

のparamsがなくても動作しDomDocument('1.0', 'UTF-8')ます。しかし、私の場合、部分的なHTMLのみがロードされます。
JKB

5

DOMDocumentには、意味のあるヘッダーを含むHTMLのバージョンをフィードする必要があります。HTML5と同じように。

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

htmlをできる限り有効にしておくとよいでしょう。クエリを開始するときに問題が発生することはありません... :-)を避け、htmlentities!!!! これは、リソースを浪費するために必要なやり取りです。コードを狂わせてください!!!!


5

私はマンジャロでphp 7.3.8を使用していて、ペルシャのコンテンツで作業していました。これは私の問題を解決しました:

$html = 'hi</b><p>سلام<div>の家庭に、9 ☆';
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
print $doc->saveHTML($doc->documentElement) . PHP_EOL . PHP_EOL;

これとまったく同じアドバイスが、サムが何年も前にこの同じページで与えたものです。冗長な情報を投稿しないでください。
mickmackusa

4

Works finde for me:

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

2
注意してください、utf8_decodeは情報を失う可能性があります(に置き換えられます?
jwal

2

正しい結果を得るために使用してください

$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の後に2回変換されることはありません。これはXSSと不正なHTMLの穴です。


1

私のために働いた唯一のものは、受け入れられた答えでした

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

これは、部分的なドキュメント(2つの<p>タグを持つドキュメントなど)では機能せず、<p>タグの1つだけが返されます。


0

問題は、DOMDocument :: saveHTML()関数にパラメーターを追加すると、エンコードが失われることです。場合によっては、パラメーターの使用を避け、古い文字列関数を使用して探しているものを見つける必要があります。

前の答えはあなたにとってはうまくいくと思いますが、この回避策は私にとってはうまくいかなかったので、私の答えである可能性のある人を助けるためにその答えを追加します。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.