Проблемы с кодировкой DOMDocument / преобразование символов - PullRequest
7 голосов
/ 19 августа 2010

Я использую DOMDocument для манипулирования / изменения HTML, прежде чем он получит вывод на страницу. Это только фрагмент HTML, а не полная страница. Моя первоначальная проблема заключалась в том, что все французские персонажи испортились, что я смог исправить после некоторых проб и ошибок. Теперь кажется, что остается только одна проблема: «персонаж превращается в? .

код:

<?php
    $dom = new DOMDocument('1.0','utf-8');
         $dom->loadHTML(utf8_decode($row->text));

         //Some pretty basic modification here, not even related to text

         //reinsert HTML, and make sure to remove DOCTYPE, html and body that get added auto.
         $row->text = utf8_encode(preg_replace('/^<!DOCTYPE.+?>/', '', str_replace( array('<html>', '</html>', '<body>', '</body>'), array('', '', '', ''), $dom->saveHTML())));
?>

Я знаю, что с декодированием / кодированием utf8 становится все хуже, но это единственный способ, которым я мог до сих пор работать. Вот пример строки:

Ввод: Sans doute parce qu’il vient d’atteindre и до свидания в детях

Выход: Sans Doute Parce Quil Vient D? Attainind Une Date D & Eacute, Terminte Dans Son Spectulaire Cheminement

Если я найду больше деталей, я добавлю их. Спасибо за ваше время и поддержку!

Ответы [ 4 ]

16 голосов
/ 19 августа 2010

Не используйте utf8_decode.Если ваш текст в UTF-8, передайте его так.

К сожалению, DOMDocument по умолчанию LATIN1 в случае HTML.Такое поведение выглядит следующим образом:

  • При извлечении удаленного документа он должен определить кодировку из заголовков
  • Если заголовок не был отправлен или файл является локальным, ищитесоответствующий метаэквивалент
  • В противном случае по умолчанию используется LATIN1.

Пример работы:

<?php
$s = <<<HTML
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
Sans doute parce qu’il vient d’atteindre une date déterminante
dans son spectaculaire cheminement
</body>
</html>
HTML;

libxml_use_internal_errors(true);
$d = new domdocument;
$d->loadHTML($s);

echo $d->textContent;

И с XML (по умолчанию UTF-8):

<?php
$s = '<x>Sans doute parce qu’il vient d’atteindre une date déterminante'.
    'dans son spectaculaire cheminement</x>';
libxml_use_internal_errors(true);
$d = new domdocument;
$d->loadXML($s);

echo $d->textContent;
7 голосов
/ 11 октября 2012

loadHtml() не всегда распознает правильную кодировку, указанную в метатеге HTTP-EQUIV типа содержимого.

Если хаки DomDocument('1.0', 'UTF-8') и loadHTML('<?xml version="1.0" encoding="UTF-8"?>' . $html) не работают так, как онине для меня (PHP 5.3.13), попробуйте следующее:

Добавьте другой раздел <head> сразу после открывающего тега <html> с правильным метатегом HTTP-EQUIV типа содержимого.Затем позвоните loadHtml(), затем удалите лишний тег <head>.

// Ensure entire page is encoded in UTF-8
$encoding = mb_detect_encoding($body);
$body = $encoding ? @iconv($encoding, 'UTF-8', $body) : $body;

// Insert a head and meta tag immediately after the opening <html> to force UTF-8 encoding
$insertPoint = false;
if (preg_match("/<html.*?>/is", $body, $matches, PREG_OFFSET_CAPTURE)) {
    $insertPoint = mb_strlen( $matches[0][0] ) + $matches[0][1];
}
if ($insertPoint) {
    $body = mb_substr(
        $body,
        0,
        $insertPoint
    ) . "<head><meta http-equiv='Content-type' content='text/html; charset=UTF-8' /></head>" . mb_substr(
        $body,
        $insertPoint
    );
}
$dom = new DOMDocument();

// Suppress warnings for loading non-standard html pages
libxml_use_internal_errors(true);
$dom->loadHTML($body);
libxml_use_internal_errors(false);

// Now remove extra <head>

См. Эту статью: http://devzone.zend.com/1538/php-dom-xml-extension-encoding-processing/

4 голосов
/ 22 августа 2014

Мне этого было достаточно, остальные ответы здесь были излишними.Учитывая, что у меня есть HTML-документ с существующим тегом HEAD.Теги HEAD не имеют атрибутов, и у меня не было проблем с добавлением дополнительного тега META в HTML для моего варианта использования.

1 голос
/ 14 ноября 2018

Как уже отмечали другие, DOMDocument и LoadHTML будут по умолчанию использовать кодировку LATIN1 с фрагментами HTML. Это также обернет ваш HTML что-то вроде этого:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>YOUR HTML</body></html>

Так что, как уже отмечали другие, вы можете исправить кодировку, вставив элемент HEAD в ваш HTML с элементом META, который содержит правильную кодировку.

Однако, если вы работаете с фрагментом HTML, вы, вероятно, не хотите, чтобы было выполнено обтекание, и не хотите сохранять вставленный вами элемент HEAD.

Следующий код вставит элемент HEAD, а затем после обработки с помощью регулярного выражения удалит все элементы переноса:

<?php
$html = '<article class="grid-item"><p>Hello World</p></article><article class="grid-item"><p>Goodbye World</p></article>';
$head = '<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head>';

libxml_use_internal_errors(true);
$dom = new DOMDocument('1.0', 'utf-8');
$dom->loadHTML($head . $html);
$xpath = new DOMXPath($dom);

// Loop through all article.grid-item elements and add the "invisible" class to them
$nodes = $xpath->query("//article[contains(concat(' ', normalize-space(@class), ' '), ' grid-item ')]");
foreach($nodes as $node) {
  $class = $node->getAttribute('class');
  $class .= ' invisible';
  $node->setAttribute('class', $class);
}

$content = preg_replace('/<\/?(!doctype|html|head|meta|body)[^>]*>/im', '', $dom->saveHTML());
libxml_use_internal_errors(false);

echo $content;
?>
...