54

Webサーバーはutf-8エンコーディングで応答を提供し、すべてのファイルはutf-8エンコーディングで保存され、設定について知っていることはすべてutf-8エンコーディングに設定されています。

出力が機能するかどうかをテストするための簡単なプログラムを次に示します。

<?php
$html = <<<HTML
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Test!</title>
</head>
<body>
    <h1>☆ Hello ☆ World ☆&lt;/h1>
</body>
</html>
HTML;

$dom = new DOMDocument("1.0", "utf-8");
$dom->loadHTML($html);

header("Content-Type: text/html; charset=utf-8");
echo($dom->saveHTML());

プログラムの出力は次のとおりです。

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Test!</title></head><body>
    <h1>&acirc;&#152;&#134; Hello &acirc;&#152;&#134; World &acirc;&#152;&#134;</h1>
</body></html>

次のようにレンダリングされます:

☆こんにちは☆世界☆</ h1>

何が間違っているのでしょうか?UTF-8を適切に処理するようにDOMDocumentに指示するには、どれだけ具体的にする必要がありますか?

4

3 に答える 3

116

DOMDocument::loadHTML()HTML文字列が必要です。

HTMLは、ISO-8859-1仕様に従ってデフォルトとしてエンコーディング(ISOラテンアルファベットNo. 1)を使用します。それ以降は長くなります。6.1を参照してください。HTMLドキュメントの文字セット。実際には、これWindows-1252が一般的なWebブラウザでのデフォルトのサポートです。

PHPのDOMDocumentはlibxmlに基づいており、HTML 4.0用に設計されたHTMLparserをもたらすため、ここまでさかのぼります。

ISO-8859-1エンコードされた文字列をロードできると仮定しても安全だと思います。

文字列はUTF-8エンコードされています。127 / h7Fより高いすべての文字をHTMLエンティティに変換すれば、問題ありません。自分でそれを行いたくない場合はmb_convert_encodingHTML-ENTITIESターゲットエンコーディングでそれが行われます。

  • 名前付きエンティティを持つキャラクターは、名前付きエンティティを取得します。€ -> &euro;
  • 他の人は彼らの数値(10進数)エンティティを取得します、例えば☆ -> &#9734;

以下は、コールバック関数を使用して進行状況をもう少しわかりやすくするコード例です。

$html = preg_replace_callback('/[\x{80}-\x{10FFFF}]/u', function($match) {
    list($utf8) = $match;
    $entity = mb_convert_encoding($utf8, 'HTML-ENTITIES', 'UTF-8');
    printf("%s -> %s\n", $utf8, $entity);
    return $entity;
}, $html);

文字列のこの例示的な出力:

☆ -> &#9734;
☆ -> &#9734;
☆ -> &#9734;

とにかく、それはあなたのストリングをより深く調べるためだけのものです。あなたはそれを処理できるエンコーディングに変換してもらいたいと思っていますloadHTMLUS-ASCIIこれは、外部のすべてをHTMLエンティティに変換することで実行できます。

$us_ascii = mb_convert_encoding($utf_8, 'HTML-ENTITIES', 'UTF-8');

入力が実際にはUTF-8でエンコードされていることに注意してください。エンコーディングが混在している場合(一部の入力で発生する可能性があります)mb_convert_encoding、文字列ごとに1つのエンコーディングしか処理できません。正規表現を使用して文字列をより具体的に置換する方法については、すでに上記で概説したので、ここではさらに詳しく説明します。

もう1つの方法は、エンコーディングをヒントにすることです。これは、ドキュメントを変更して追加することで実行できます。

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

これは、文字セットを指定するContent-Typeです。これは、Webサーバーを介して利用できないHTML文字列のベストプラクティスでもあります(たとえば、ディスクに保存されているか、例のように文字列内に保存されています)。Webサーバーは通常、それを応答ヘッダーとして設定します。

置き忘れた警告を気にしない場合は、文字列の前に追加するだけです。

$dom = new DomDocument();
$dom->loadHTML('<meta http-equiv="content-type" content="text/html; charset=utf-8">'.$html);

HTML 2.0の仕様<head>により、ドキュメントのセクションにのみ表示できる要素が自動的にそこに配置されます。これはここでも起こります。出力(きれいな印刷):

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta charset="utf-8">
    <title>Test!</title>
  </head>
  <body>
    <h1>☆ Hello ☆ World ☆&lt;/h1>    
  </body>
</html>
于 2012-07-03T11:47:59.990 に答える
17

HTMLドキュメントをDOMDocumentにロードした後、元のエンコーディングを設定する(またはリセットしたほうがよい)だけです。サンプルコードは次のとおりです。

$dom = new DOMDocument();
$dom->loadHTML('<?xml encoding="UTF-8">' . $html);

foreach ($dom->childNodes as $item)
    if ($item->nodeType == XML_PI_NODE)
        $dom->removeChild($item);
$dom->encoding = 'UTF-8'; // reset original encoding
于 2013-06-05T04:55:03.193 に答える
11
<?php
  header("Content-type: text/html; charset=utf-8");
  $html = <<<HTML
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Test!</title>
</head>
<body>
    <h1>☆ Hello ☆ World ☆&lt;/h1>
</body>
</html>
HTML;

  $html = mb_convert_encoding($html, 'HTML-ENTITIES', "UTF-8");
  $dom = new DOMDocument("1.0", "utf-8");
  $dom->loadHTML($html);

  header("Content-Type: text/html; charset=utf-8");
  echo($dom->saveHTML());

出力:

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Test!</title></head><body>
    <h1>&#9734; Hello &#9734; World &#9734;</h1>
</body></html>
于 2012-07-03T10:52:54.790 に答える