【问题标题】:How to print UTF-8 correctly using XML::LibXML如何使用 XML::LibXML 正确打印 UTF-8
【发布时间】:2016-10-10 04:34:36
【问题描述】:

我编写了一个脚本,它将散列转换为 XML,并从 XML::LibXML::Document 返回 toString 函数的结果。如果不破坏 XML 文档中的 UTF-8 字符,我似乎不可能打印结果(字符串)。

我尝试将binmode 用于STDOUT,并使用/避免setEncoding 用于XML 文档。两者都没有达到我的预期。

对此的唯一“解决方案”是使用$XML::LibXML::skipXMLDeclaration = 1;decode 禁用XML 声明,然后再将其返回到要打印的脚本。

我只是没有得到这个声明行的部分。我什至可以将字符串(使用此声明行)返回到我的第二个脚本,将其写入没有任何残缺字符的文件,如果我直接打印到 STDOUT,则会得到残缺字符。如上所述,我什至尝试过使用binmode

那么,toString 或一般将 XML 打印到 STDOUT 有什么问题?我错过了什么吗?如果需要,我当然可以提供一些代码。

在这种特定情况下,我刚刚从XML::Simple 切换到XML::LibXML,它与XML::Simple 配合得很好。我知道XML::Simple 不是很好;我只是很好奇为什么它工作得很好,XML::LibXML 让我忙了一整天。


这是我正在使用的代码示例。老实说,这个 sn-p 就像一个魅力,我仍在努力寻找与我的真实脚本的区别,我显然不能在这里发布。但是为了向你展示我在做什么,这可能没问题:

create_xml.pm

#!/usr/bin/perl

package create_xml;

use XML::LibXML;
use utf8;

sub convertHash
{
    my ($hash) = @_;

    my $xmlDoc = XML::LibXML::Document->new();
    $xmlDoc->setEncoding('UTF-8');

    my $xmlRoot = $xmlDoc->createElement('TestXML');

    foreach my $key (keys %$hash)
    {
        my $wert = $hash->{$key};
        my $element = $xmlDoc->createElement($key);
        $element->appendTextNode($wert);
        $xmlRoot->appendChild($element);
    }

    my $wsStatusElement = $xmlDoc->createElement('FixedElement');
    $wsStatusElement->appendTextNode(123);
    $xmlRoot->appendChild($wsStatusElement);

    $xmlDoc->setDocumentElement($xmlRoot);

    open(XML, '>', 'test1.xml');
        print XML $xmlDoc->toString(1);
    close(XML);

    return $xmlDoc->toString(1);
}

1;

print_xml.pl

#!/usr/bin/perl

use lib '.';
use create_xml;
use utf8;

my %testhash;
$testhash->{'A'} = 15;
$testhash->{'B'} = 'abc';
$testhash->{'C'} = 'äöüä';
$testhash->{'D'} = '€';

my $xml = create_xml::convertHash($testhash);

open(XML, '>', 'test2.xml');
    print XML $xml;
close(XML);

print $xml;

使用此脚本,两个编写的 xml 文件都很好,但最后一次打印的输出被破坏了。我的€ 看起来像这样<D>â�¬</D>。如前所述,这个 sn-p 工作。在发现差异后,我将稍后编辑我的问题。


我终于找到了问题所在。我使用了一个 perlmodule,上面写着use open ':std', ':encoding(UTF-8)';。那么为什么这会破坏我的打印字符,而不是写入文件呢?我仍然很困惑,觉得我错过了一些东西。这是已经 utf8 编码的字符被编码两次的部分吗?有没有重置的选项?

【问题讨论】:

  • 我认为我们需要查看一个产生此问题的输入 XML 和 Perl 代码示例。对“致残”的更好描述也会有所帮助。您在哪个平台上工作,您如何检查程序的输出?
  • 我附加了一个 sn-p,但现在这个 sn-p 正在工作。这让我很困惑,我会进一步调查。也许有人已经看到了,我的代码可能有什么问题;)

标签: xml perl utf-8 libxml2


【解决方案1】:

它对我有用。

$ perl -Mutf8 -MXML::LibXML -we '
    my $xml =q{<?xml version="1.1" encoding="utf-8"?><r>ěščřž</r>};
    my $doc = XML::LibXML->load_xml(string => $xml);
    binmode STDOUT, "encoding(UTF-8)";
    print $doc->findvalue("r"), "\n";
'
ěščřž

使用toString,需要使用Encodedecode

print decode("utf-8", $doc->toString), "\n";

【讨论】:

  • toString 的结果如果直接打印到需要 UTF-8 的设备上应该没问题。它只是在 Perl 中用作字符串的用处不大
【解决方案2】:

$doc-&gt;toString 返回一个已经编码的字符串。使用的编码是$doc-&gt;setEncoding 设置并由$doc-&gt;actualEncoding 返回的编码。默认情况下,新文档为 UTF-8,现有文档为原始编码。

因此,您只需将$doc-&gt;toString 的输出打印到STDOUT,而无需先将编码层添加到STDOUT。

$ perl -e'
   use strict;
   use warnings;
   use XML::LibXML qw( );
   my $doc = XML::LibXML::Document->new(1.0, "UTF-8");
   my $root = $doc->createElement("root");
   $root->appendText("\x{2660}");
   $doc->setDocumentElement($root);
   print $doc->toString();
' | od -c
0000000   <   ?   x   m   l       v   e   r   s   i   o   n   =   "   1
0000020   "       e   n   c   o   d   i   n   g   =   "   U   T   F   -
0000040   8   "   ?   >  \n   <   r   o   o   t   > 342 231 240   <   /
0000060   r   o   o   t   >  \n
0000066

很难说出您遇到了什么问题,因为您没有提供问题的演示。

最可能的问题是,当您应该传递解码文本时,您将编码文本传递给 XML::LibXML。

另一个不太可能的可能性是您与bug 发生冲突,appendText 和其他向文档添加文本的方法会受到影响。您可以通过“升级”您的文本来解决该错误。

   sub _u($) { my $s = shift; utf8::upgrade($s); $s }

   $root->appendText("\x{C9}ric");       # Buggy
   $root->appendText(_u("\x{C9}ric"));   # Ok

【讨论】:

    猜你喜欢
    • 2016-12-14
    • 1970-01-01
    • 1970-01-01
    • 2018-05-12
    • 2015-05-15
    • 1970-01-01
    • 2014-02-01
    • 1970-01-01
    • 2011-01-08
    相关资源
    最近更新 更多