DOMDocument编码问题/字符转换

时间:2010-08-19 15:31:03

标签: php utf-8 domdocument

我使用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'atteind datedéterminantedansson spectaculaire cheminement

输出: Sans doute parce qu?il vient d?atteind date d'déminanantedans son spectaculaire cheminement

如果我发现更多细节,我会添加它们。感谢您的时间和支持!

4 个答案:

答案 0 :(得分:16)

请勿使用utf8_decode。如果您的文本是UTF-8,请将其传递给它。

不幸的是,如果是HTML,DOMDocument默认为LATIN1。似乎行为就是这个

  • 如果提取远程文档,则应从标题中推断出编码
  • 如果未发送标头或文件是本地文件,请查找对应的meta-equiv
  • 否则,默认为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;

答案 1 :(得分:7)

loadHtml()并不总能识别Content-type 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/

答案 2 :(得分:4)

这对我来说已经足够了,这里的其他答案都是矫枉过正的。鉴于我有一个带有现有HEAD标签的HTML文档。 HEAD标签没有属性,我没有问题在我的用例的HTML中留下额外的META标签。

$data = str_ireplace('<head>', '<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />', $data);
$document = new DOMDocument();
$document->loadHTML($data);

答案 3 :(得分:1)

正如其他人指出的那样,DOMDocumentLoadHTML将默认为带有HTML片段的LATIN1编码。它还将使用以下内容包装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元素插入包含具有正确编码的META元素的HTML中来修复编码。

但是,如果您使用的是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;
?>