如何强制XPath使用UTF8?

9

我有一个XHTML文档通过Greasemonkey AJAX传递给PHP应用程序。PHP应用程序使用UTF8。如果我将POST内容直接输出到接收div中的textarea,一切仍然以UTF8正确编码。

当我尝试使用XPath解析时

$dom = new DOMDocument();
$dom->loadHTML($raw2);
$xpath = new DOMXPath($dom);
$query = '//td/text()';
$nodes = $xpath->query($query);
foreach($nodes as $node) {
  var_dump($node->wholeText);
}

转储的字符串不是UTF8编码。我该如何强制DOM/XPath使用UTF8?

你能提供一个(经过测试的)HTML文档示例吗? - VolkerK
5个回答

36

我曾遇到同样的问题,无法在我的web服务器上使用Tidy。我找到了以下解决方案,它有效地解决了这个问题:

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

+1'd,唯一的建议是将第二行移至顶部,它对我来说有点混淆。 - Nabil Kadimi
2
我苦苦寻找了一年多的时间,时断时续地努力解决这个问题。非常感谢您的帮助。在此之前,我尝试过无数方法,包括特殊类、头文件、元数据、php.ini文件、XML UTF-8的hack等等,但对于我的问题都没有用,唯独这个有效。 - James Huckabone

6

虽然时间有点晚,但或许它能对某些人有所帮助...

问题可能出在输出上,而不是dom/xpath对象本身。

如果您直接输出nodeValue,您将得到损坏的字符,例如:

ìÂÂì ë¹Â디ì¤
ìì ë¹ë””ì¤ í°ì  íì¤

您需要使用第二个参数"utf-8"来加载您的dom对象,new \DomDocument('1.0', 'utf-8'),但是当您打印dom节点列表/元素值时,您会得到乱码字符: echo $contentItem->item($index)->nodeValue 您需要使用 utf8_decode 将其包装起来: echo utf8_decode($contentItem->item($index)->nodeValue) //输出结果:者不終朝而會,愚者可浹旬而學

请不要在多个问题中添加相同的答案。回答最好的一个,并将其余的标记为重复。请参阅http://meta.stackexchange.com/questions/104227/is-it-acceptable-to-add-a-duplicate-answer-to-several-questions - Bhargav Rao

4
如果它是一个完全成熟的有效的xhtml文档,你不应该使用loadhtml(),而应该使用load() / loadxml()。 给定示例xhtml文档:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <title>xhtml test</title>
    </head>
    <body>
        <h1>A Table</h1>
        <table>
            <tr><th>A</th><th>O</th><th>U</th></tr>
            <tr><td>Ä</td><td>Ö</td><td>Ü</td></tr>
            <tr><td>ä</td><td>ö</td><td>ü</td></tr>
        </table>
    </body>
</html>

脚本
<?php
$raw2 = 'test.html';

$dom = new DOMDocument();
$dom->load($raw2);
$xpath = new DOMXPath($dom);
var_dump($xpath->registerNamespace('h', 'http://www.w3.org/1999/xhtml'));
$query = '//h:td/text()';
$nodes = $xpath->query($query);
foreach($nodes as $node) {
    foo($node->wholeText);
}


function foo($s) {
    for($i=0; $i<strlen($s); $i++) {
        printf('%02X ', ord($s[$i]));
    }
    echo "\n";
}

打印

bool(true)
C3 84 
C3 96 
C3 9C 
C3 A4 
C3 B6 
C3 BC 

即输出/字符串采用utf-8编码。

我正在解析的网页没有 <?xml ?>。使用 Tidy 添加后,我的问题得到解决。 - Gordon
没错。我坚持这个强烈的观点(但保持弱化):如果它声称是xhtml,请不要试图修复它;他们想要前面的x,就必须交付。;-) - VolkerK

1

我没有尝试过,但是DOMDocument::__construct的第二个参数似乎与编码有关;也许这会对你有所帮助 :-)

否则,DOMDocument中有一个可写的编码属性。

DOMXpath是使用DOMDocument作为参数构建的,也许它会起作用...


$dom->encoding = 'utf8' 没有任何效果,即使在 __construct() 中设置了编码也是如此。可能是由于使用了 loadHTML(),但我不确定。 - Gordon
1
loadHTML()覆盖了构造函数中设置的编码。 - leticia

0

曾经遇到过类似的问题(无法强制Xpath在loadHTML中使用UTF-8),最终这篇优秀的文章提供了解决方案:http://devzone.zend.com/article/8855

解决方法:

在开头的<head>标签后面立即插入一个额外的<meta>标签,其中包含适当的Content-type HTTP-EQUIV元标记。


1
这个链接已经失效了。你能否更新一下它或者把那个页面的解决方案粘贴在这里? - user658182

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接