PHP DOMDocument loadHTML неправильно кодирует UTF-8 - PullRequest
166 голосов
/ 22 ноября 2011

Я пытаюсь разобрать некоторый HTML с помощью DOMDocument, но когда я это делаю, я вдруг теряю свою кодировку (по крайней мере, так мне кажется).

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

Ответы [ 10 ]

426 голосов
/ 22 ноября 2011

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 (например, катане), это самая безопасная альтернатива.

53 голосов
/ 19 декабря 2013

Проблема с saveHTML() и saveXML(), оба они не работают правильно в Unix.Они не сохраняют символы UTF-8 правильно при использовании в Unix, но работают в 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));

Примечание

  1. Английские символы не вызывают проблем при использовании saveHTML() без параметров (поскольку английские символы сохраняются как однобайтовые символы в UTF-8)

  2. Проблема возникает, когда у вас есть многобайтовые символы (например, китайский, русский, арабский, иврит и т. Д.)

Я рекомендую прочитать эту статью: http://coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/. Вы поймете, как работает UTF-8 и почему у вас возникла эта проблема.Это займет у вас около 30 минут, но это хорошо проведенное время.

14 голосов
/ 22 ноября 2011

Убедитесь, что реальный исходный файл сохранен как UTF-8 (Вы можете даже попробовать не рекомендованные символы спецификации с UTF-8, чтобы убедиться).

Также в случае HTML убедитесь, что вы объявили правильную кодировку, используя теги meta:

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

Если это CMS (поскольку вы пометили свой вопрос в Joomla), вам может потребоваться настроить соответствующие параметры для кодировки.

10 голосов
/ 22 ноября 2015

Вы можете добавить префикс строки, обеспечивающий кодирование utf-8, например:

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

И затем вы можете продолжить с уже имеющимся кодом, например:

$doc->saveXML()
7 голосов
/ 20 ноября 2017

Мне потребовалось некоторое время, чтобы понять, но вот мой ответ.

Перед использованием 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
}

Это не помогло сохранить кодировку UTF-8, несмотря на правильные мета-теги, настройки php и все остальные средства, предлагаемые здесьв другом месте.Вот что работает:

$dom = new DomDocument('1.0', 'UTF-8');
$str = file_get_contents($url);
if ($dom->loadHTML(mb_convert_encoding($str, 'HTML-ENTITIES', 'UTF-8')) == false) {
}

и т. Д.Теперь с миром все в порядке.Надеюсь, это поможет.

5 голосов
/ 04 июня 2013

Вы должны передать DOMDocument версию вашего HTML с заголовком, который имеет смысл. Так же, как HTML5.

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

возможно, это хорошая идея, чтобы ваш html был настолько корректным, насколько это возможно, чтобы не возникало проблем при запуске запроса ... вокруг :-) и держаться подальше от htmlentities !!!! Это необходимая трата ресурсов назад и вперед. держи свой код безумным !!!!

4 голосов
/ 23 июня 2016

Работы для меня:

$dom = new \DOMDocument;
$dom->loadHTML(utf8_decode($html));
...
return  utf8_encode( $dom->saveHTML());
1 голос
/ 15 июня 2016

Используйте его для правильного результата

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

Это плохой способ, потому что специальные символы, такие как <;>;может быть в профиле $, и они не будут преобразованы дважды после mb_convert_encoding.Это дыра для XSS и неверного HTML.

0 голосов
/ 16 мая 2013

Проблема в том, что при добавлении параметра в функцию DOMDocument :: saveHTML () вы теряете кодировку.В некоторых случаях вам нужно избегать использования параметра и использовать старую строковую функцию, чтобы найти то, что вы ищете.

Я думаю, что предыдущий ответ работает для вас, но так как этот обходной путь не помогэто не работает для меня, я добавляю этот ответ, чтобы помочь людям, которые могут быть в моем случае.

0 голосов
/ 22 ноября 2011

Попробуйте использовать utf8_encode

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...