【问题标题】:Why is Perl XML::LibXML changing UTF8 to 8859-1?为什么 Perl XML::LibXML 将 UTF8 更改为 8859-1?
【发布时间】:2016-08-11 13:41:25
【问题描述】:

有了这个输入文件

<?xml version="1.0" encoding="UTF-8"?>
<entry>
   <title>ú</title>
</entry>

还有这段代码,

my $raw_xml = read_file("test.xml", binmode => 'raw');
print "$raw_xml\n";
$raw_xml =~ /<title>(.*?)</;
print "Regex finds [$1]\n";      # prints u+accent to UTF8 terminal

my $dom  = XML::LibXML->load_xml(string => $raw_xml);
my $xpc = XML::LibXML::XPathContext->new($dom);
my ($entry) = $xpc->findnodes('entry');
my $title = $xpc->findvalue('title', $entry) || '';

print "title is now [$title]\n"; # prints garbage character to UTF8 terminal, u+accent to ISO-8859-1 terminal

完美的 utf8 在哪里/为什么被翻译成 8 位字符集之一(我假设它是 8859-1,可能是 cp1252 等)?

我通过 Google 找到的所有内容都表明它应该从头到尾都是 utf8。但显然不是。

注意:如果我使用 binmode 在文件句柄上打开文件并将其传递给 load_xml,则行为完全相同;我碰巧在从中提取的真实代码中将 xml 保存在内存中 - 这也意味着我可以使用上面的正则表达式进行验证。

【问题讨论】:

    标签: xml perl utf-8


    【解决方案1】:

    您有两个错误可以抵消以在第一次测试中产生正确的输出。


    您的自制解析器无法解码文档

    您可以通过将/&lt;title&gt;(.*?)&lt;/ 更改为/&lt;title&gt;(.)&lt;/ 来观察此错误。它没有按预期获取第一个字形 (ú),而是仅获取其编码的第一个字节 (C3)。

    要解决此问题,请替换

    $raw_xml =~ /<title>(.*?)</;
    print "Regex finds [$1]\n";
    

    use Encode qw( decode_utf8 );
    
    my $decoded_xml = decode_utf8($raw_xml);
    $decoded_xml =~ /<title>(.*?)</;
    print "Regex finds [$1]\n";
    

    现在您从两个测试中得到相同的行为,即相同的垃圾输出。这就引出了第二个问题。


    您不对输出进行编码

    XML::LibXML 返回解码文本,即 Unicode 代码点。 ú 因此作为字符 FA 返回,因为 ú 是 U+000FA。这是正确的,因为您不必关心编码,除非在执行 I/O 时。

    问题发生在执行 I/O 时。 print 期望它接收到的每个字符都代表一个字节,所以当你告诉它打印字符 FA 时,它会打印字节 FA,然后你的终端会“wtf?”。

    您的终端需要 UTF-8,因此您需要使用 UTF-8 对字符串进行编码,然后再将其传递给 print,或者告诉 print 为您执行此操作。

    # Decode STDIN (UTF-8).
    # Decode STDOUT and STDERR (UTF-8).
    # The default encoding for files opened in scope is UTF-8.
    use open ':std', ':encoding(UTF-8)';
    

    完整的解决方案:

    use open ':std', ':encoding(UTF-8)';
    
    use Encode qw( decode_utf8 );
    
    my $raw_xml = read_file("test.xml", binmode => 'raw');
    
    {
       my $decoded_xml = decode_utf8($raw_xml);
       my ($title) = $decoded_xml =~ /<title>(.*?)</;
       printf("%s: [%s] [%s]\n", "Home-grown", $title, substr($title, 0, 1));
    }
    
    {
       my $doc = XML::LibXML->load_xml(string => $raw_xml );
       my ($entry_node) = $doc->findnodes('entry');
       my $title = $entry->findvalue('title');
       printf("%s: [%s] [%s]\n", "LibXML", $title, substr($title, 0, 1));
    }
    

    【讨论】:

    • 谢谢!由于您的回答和一些实验,perl 字符集拼图的缺失部分对我来说已经到位,我非常感激。
    【解决方案2】:

    Latin-1 是 Perl 的默认编码,特别是对于源代码中的字符串。 raw 编码适用于图像或视频等二进制数据。如果您将数据作为原始数据读取,则它没有编码。如果你连接一个有编码的字符串和没有编码的原始数据,Perl 必须猜测原始数据的编码。不要将字符串视为原始数据。如果您仍想这样做,请在将原始数据附加到字符串之前告诉 Perl 编码。

    【讨论】:

    • 没错,但 LibXML 的 pod link 明确表示 ... the functions related to I/O operations (i.e. parsing and saving) operate with binary data (in the original document encoding) obeying the encoding declaration of the XML documents.... Do NOT apply any encoding-related PerlIO layers (:utf8 or :encoding(...)) to file handles that are an input for the parses or an output for a serializer of (full) XML documents. ... `
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-19
    • 2014-08-21
    • 1970-01-01
    • 2016-02-10
    • 2012-03-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多