【发布时间】:2010-08-26 12:40:32
【问题描述】:
当我尝试使用 DOMDocument 将 UTF-8 字符串写入 XML 文件时,它实际上写入的是字符串的十六进制表示法,而不是字符串本身。
例如:
ירושלים
代替:
ירושלים
有解决问题的方法吗?
【问题讨论】:
标签: php utf-8 domdocument hebrew
当我尝试使用 DOMDocument 将 UTF-8 字符串写入 XML 文件时,它实际上写入的是字符串的十六进制表示法,而不是字符串本身。
例如:
ירושלים
代替:
ירושלים
有解决问题的方法吗?
【问题讨论】:
标签: php utf-8 domdocument hebrew
好的,给你:
$dom = new DOMDocument('1.0', 'utf-8');
$dom->appendChild($dom->createElement('root'));
$dom->documentElement->appendChild(new DOMText('ירושלים'));
echo $dom->saveXml();
可以正常工作,因为在这种情况下,您构建的文档将保留指定为第二个参数的编码:
<?xml version="1.0" encoding="utf-8"?>
<root>ירושלים</root>
但是,一旦您将 XML 加载到未指定编码的 Document 中,您将丢失您在构造函数中声明的任何内容,这意味着:
$dom = new DOMDocument('1.0', 'utf-8');
$dom->loadXml('<root/>'); // missing prolog
$dom->documentElement->appendChild(new DOMText('ירושלים'));
echo $dom->saveXml();
不会有 utf-8 的编码:
<?xml version="1.0"?>
<root>ירושלים</root>
所以如果你 loadXML 东西,确保它是
$dom = new DOMDocument();
$dom->loadXml('<?xml version="1.0" encoding="utf-8"?><root/>');
$dom->documentElement->appendChild(new DOMText('ירושלים'));
echo $dom->saveXml();
它会按预期工作。
您也可以在加载文档后specify the encoding。
【讨论】:
如果你想用 DOMDocument 输出 UTF-8,你需要指定它。很简单,不是吗?如果您已经闻到了一个棘手的问题,那么您并没有走得太远,但乍一看,它确实是直截了当的。
考虑以下输出十六进制实体的(UTF-8 编码)代码示例:
$dom = new DOMDocument();
$dom->loadXml('<root>ירושלים</root>');
$dom->save('php://output');
输出:
<?xml version="1.0"?>
<root>ירושלים</root>
正如写的那样,如果你想将它输出为 UTF-8,你需要指定它,它是直截了当的:
...
$dom->encoding = 'UTF-8';
$dom->save('php://output');
然后输出是 UTF-8 显式:
<?xml version="1.0" encoding="UTF-8"?>
<root>ירושלים</root>
直截了当的部分就这么多。如果您对肮脏的小细节感兴趣,可以继续阅读 - 如果没有,请不要问“为什么?” :)。
我只是在 UTF-8 显式“ 中写了 ",因为在第一个示例中,输出也是 UTF-8 编码的,XML 只包含完美的十六进制实体有效 - 即使在 UTF-8 中!
您已经注意到我从这里开始挑剔,但请记住:UTF-8 is the default encoding of XML。
如果你现在开始说:嘿等等,如果默认编码仍然是 UTF-8,为什么 PHP 的 DOMDocument 首先使用实体?
事实上,它确实不与问题中的发现相反。不是总是。
请参阅以下示例,该示例使用 XML 注释而不是包含 Ivrit 字母的节点值:
$dom = new DOMDocument();
$dom->loadXml('<root><!-- ירושלים --></root>');
$dom->save('php://output');
输出:
<?xml version="1.0"?>
<root><!-- ירושלים --></root>
好的,都清楚了吗?所以这里有一个肮脏的小秘密:不管你有没有这些 XML 实体——对于文档来说它没有任何区别,它只是编写相同 XML 字符数据的不同形式。并且您已经感受到了邀请:让我们试试 CDATA 来代替第一个示例:
$dom = new DOMDocument();
$dom->loadXML("<root><![CDATA[ירושלים]]></root>");
$dom->save('php://output');
输出:
<?xml version="1.0"?>
<root><![CDATA[ירושלים]]></root>
正如前面的 XML 注释示例一样,这里没有使用 XML 实体。好吧,它们无论如何都是无效的,就像 XML 注释示例一样。
对于概述,让我们创建一个包含所有这些的示例:
$dom = new DOMDocument();
$dom->loadXML("<!-- ירושלים --><root>ירושלים <![CDATA[ירושלים]]></root>");
$dom->save('php://output');
输出:
<?xml version="1.0"?>
<!-- ירושלים -->
<root>ירושלים <![CDATA[ירושלים]]></root>
经验教训:
提示:如果您的字符串包含与字符串编码不匹配的 XML 声明,或者您希望在加载前 将字符串转换为 DOMDocument 您需要更改 XML 声明和/或重新编码字符串。通过展示
XMLRecoderclass 的工作原理,in an answer 已涵盖PHP XMLReader, get the version and encoding 问题。
希望就是这样。
[1] 可能如果您从 HTTP 请求加载并提供流上下文并通过元数据标记字符编码 - 但我不知道这应该首先测试。 BOM 不起作用在某种程度上表明所有这些事情都不起作用。
【讨论】:
显然将 documentElement 作为 $node 传递给 saveXML 可以解决这个问题,尽管我不能说我明白为什么。
例如
$dom->saveXML($dom->documentElement);
而不是:
$dom->saveXML();
来源:http://www.php.net/manual/en/domdocument.savexml.php#88525
【讨论】:
$dom 对象没有在构造函数中使用文档的字符集进行初始化,但文档元素在其上下文中确实具有某种编码意义。
答案是:
当你的函数开始时,在你得到内容之后,这样做:
$content = mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8');
然后启动新文档等。以这个为例:
if ( empty( $content ) ) {
return false;
}
$doc = new DOMDocument('1.0', 'utf-8');
libxml_use_internal_errors(true);
$doc->LoadHTML($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
然后对您的代码执行您打算执行的任何操作。
【讨论】:
$doc = new DOMDocument();
$doc->loadHTML('<?xml encoding="UTF-8">' . $html);
// dirty fix
foreach ($doc->childNodes as $item)
if ($item->nodeType == XML_PI_NODE)
$doc->removeChild($item); // remove hack
$doc->encoding = 'UTF-8'; // insert proper
【讨论】:
当我创建用于编写的 DOMDocument 时,我添加了以下参数:
dom = new DOMDocument('1.0','utf-8');
这些参数导致 UTF-8 字符串按原样写入。
【讨论】: