12

当我尝试使用 DOMDocument 将 UTF-8 字符串写入 XML 文件时,它实际上写入的是字符串的十六进制表示法,而不是字符串本身。

例如:

ירושלים

代替:

ירושלים

任何想法如何解决这个问题?

4

6 回答 6

18

好的,给你:

$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>&#x5D9;&#x5E8;&#x5D5;&#x5E9;&#x5DC;&#x5D9;&#x5DD;</root>

因此,如果您 loadXML 某些东西,请确保它是

$dom = new DOMDocument();
$dom->loadXml('<?xml version="1.0" encoding="utf-8"?><root/>');
$dom->documentElement->appendChild(new DOMText('ירושלים'));
echo $dom->saveXml();

它会按预期工作。

作为替代方案,您还可以在加载文档后指定编码。

于 2010-08-26T13:08:03.743 回答
6

如果要使用 DOMDocument 输出 UTF-8,则需要指定。很简单,不是吗?如果你已经闻到了一个棘手的问题,那么你离得并不远,但乍一看,它确实是直截了当的。

考虑以下输出十六进制实体的(UTF-8 编码)代码示例:

$dom = new DOMDocument();
$dom->loadXml('<root>ירושלים</root>');
$dom->save('php://output');

输出:

<?xml version="1.0"?>
<root>&#x5D9;&#x5E8;&#x5D5;&#x5E9;&#x5DC;&#x5D9;&#x5DD;</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-8XML的默认编码

如果你现在开始说:嘿等等,如果默认编码是 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-comment 示例一样。

对于概述,让我们创建一个包含所有这些的示例:

$dom = new DOMDocument();
$dom->loadXML("<!-- ירושלים --><root>&#x5D9;רושלים <![CDATA[ירושלים]]></root>");
$dom->save('php://output');

输出:

<?xml version="1.0"?>
<!-- ירושלים -->
<root>&#x5D9;&#x5E8;&#x5D5;&#x5E9;&#x5DC;&#x5D9;&#x5DD; <![CDATA[ירושלים]]></root>

得到教训:

  • 始终使用 UTF-8。除非指定了 UTF-8 编码,否则在 PCDATA 中只使用了一些实体。如果指定了不同于 UTF-8 的编码,则适用不同的规则
  • 您无法通过将 XML 文档加载为 PHP DOMDocument本身中的 UTF-8 编码字符串来指定是否要使用实体进行输出。甚至没有libxml 标志,也没有提供 BOM。[1]
  • 您可以通过将文档编码设置为 UTF-8 来指定您不想使用实体。
  • 如果可以,您可以操作具有 XML-Declaration 的输入字符串,该字符串指定gordon 的回答中概述的文档编码。

提示:如果您的字符串具有与字符串编码不匹配的 XML 声明,或者您想在将字符串加载到DOMDocument之前更改两者中的任何一个,则需要更改 XML 声明和/或重新编码字符串。这已在对PHP XMLReader问题的回答中进行了介绍,通过展示的工作方式来获取版本和编码。XMLRecoder

希望就是这样。


[1]可能如果您从 HTTP 请求加载并提供流上下文并通过元数据标记字符编码 - 但这应该首先测试,我不知道。BOM 不起作用在某种程度上表明所有这些事情都不起作用。

于 2013-05-03T13:38:11.887 回答
3

显然将 documentElement 作为 $node 传递给 saveXML 可以解决这个问题,尽管我不能说我明白为什么。

例如

$dom->saveXML($dom->documentElement);

而不是:

$dom->saveXML();

来源:http ://www.php.net/manual/en/domdocument.savexml.php#88525

于 2010-08-26T12:52:02.260 回答
1

直截了当的回答是:

当你的函数开始时,在你得到内容之后,这样做:

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

然后做任何你打算用你的代码做的事情。

于 2015-12-17T01:54:43.450 回答
0

当我创建用于编写的 DOMDocument 时,我添加了以下参数:

dom = new DOMDocument('1.0','utf-8');

这些参数导致 UTF-8 字符串按原样写入。

于 2010-08-26T13:04:41.630 回答
0
$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
于 2013-03-25T09:56:35.433 回答