web-dev-qa-db-ja.com

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

編集:コードを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>
169
Slightly A.

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(これらの刀のように)で表現できるわけではないので、最も安全な代替手段です。

443
cmbuckley

問題は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 」と「 MarcoAurélioDele 」で検証されたとおり、次のバリエーションあなたの場合にはうまくいくかもしれません:

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

注意

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

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

この記事を読むことをお勧めします: http://coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/ 。 UTF-8の仕組みと、この問題が発生する理由を理解できます。約30分かかりますが、十分な時間です。

54
Greeso

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

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

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

CMSの場合(質問にJoomlaでタグを付けたため)、エンコードに適切な設定を構成する必要があります。

14
Hossein

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

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

そして、次のように、すでに持っているコードを続行できます。

$doc->saveXML()
10
Ivan

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

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) {
}

など。今ではすべてが世界に適しています。お役に立てれば。

7
Sam

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

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

htmlをできる限り有効な状態に保つことをお勧めします。クエリを開始するときに問題が発生しないようにしてください。..-)を避け、htmlentities !!!!から離れてください。これは、リソースを浪費するのに必要なことです。コードを狂わせてください!!!!

5

私のための作品finde:

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

正しい結果のためにそれを使用してください

$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

マンジャロで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;
1

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

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

0
copndz