【问题标题】:PHP: using DOMDocument whenever I try to write UTF-8 it writes the hexadecimal notation of itPHP:每当我尝试编写 UTF-8 时使用 DOMDocument 它会写入它的十六进制表示法
【发布时间】:2010-08-26 12:40:32
【问题描述】:

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

例如:

ירושלים

代替:

ירושלים

有解决问题的方法吗?

【问题讨论】:

标签: php utf-8 domdocument hebrew


【解决方案1】:

好的,给你:

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

它会按预期工作。

您也可以在加载文档后specify the encoding

【讨论】:

    【解决方案2】:

    如果你想用 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-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>&#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 中只使用了一些实体。 If a different to UTF-8 encoding is specified, different rules apply
    • 您无法通过 加载 将 XML 文档作为 UTF-8 编码字符串在 PHP DOMDocument 本身中指定是否要使用实体进行输出。甚至没有libxml flags 也没有提供 BOM。 [1]
    • 您可以通过将文档编码设置为 UTF-8 来指定您不想使用实体。
    • 如果可以的话,您可以操作具有 XML 声明的输入字符串,指定文档编码为 as outlined in gordon's answer

    提示:如果您的字符串包含与字符串编码不匹配的 XML 声明,或者您希望在加载前 将字符串转换为 DOMDocument 您需要更改 XML 声明和/或重新编码字符串。通过展示XMLRecoder class 的工作原理,in an answer 已涵盖PHP XMLReader, get the version and encoding 问题。

    希望就是这样。


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

    【讨论】:

      【解决方案3】:

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

      例如

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

      而不是:

      $dom->saveXML();
      

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

      【讨论】:

      • 可能的区别是 $dom 对象没有在构造函数中使用文档的字符集进行初始化,但文档元素在其上下文中确实具有某种编码意义。
      【解决方案4】:

      答案是:

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

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

      然后对您的代码执行您打算执行的任何操作。

      【讨论】:

        【解决方案5】:
        $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
        

        【讨论】:

          【解决方案6】:

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

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

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

          【讨论】:

            猜你喜欢
            • 2017-06-11
            • 2012-05-10
            • 2012-06-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-02-06
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多