Perl 如何避免在XML::LibXML中使用双UTF-8编码

Perl 如何避免在XML::LibXML中使用双UTF-8编码,perl,utf-8,libxml2,Perl,Utf 8,Libxml2,我的程序从数据源接收UTF-8编码的字符串。我需要篡改这些字符串,然后将它们作为XML结构的一部分输出。 当我序列化我的XML文档时,它将被双重编码,因此被破坏。当我只序列化根元素时,它会很好,但当然缺少头 下面是一段试图将问题可视化的代码: use strict; use diagnostics; use feature 'unicode_strings'; use utf8; use v5.14; use encoding::warnings; binmode(STDOU

我的程序从数据源接收UTF-8编码的字符串。我需要篡改这些字符串,然后将它们作为XML结构的一部分输出。 当我序列化我的XML文档时,它将被双重编码,因此被破坏。当我只序列化根元素时,它会很好,但当然缺少头

下面是一段试图将问题可视化的代码:

use strict; use diagnostics;    use feature 'unicode_strings';
use utf8;   use v5.14;      use encoding::warnings;
binmode(STDOUT, ":encoding(UTF-8)");    use open qw( :encoding(UTF-8) :std );
use XML::LibXML

# Simulate actual data source with a UTF-8 encoded file containing '¿Üßıçñíïì'
open( IN, "<", "./input" ); my $string = <IN>; close( IN ); chomp( $string );
$string = "Value of '" . $string . "' has no meaning";

# create example XML document as <response><result>$string</result></response>
my $xml = XML::LibXML::Document->new( "1.0", "UTF-8" );
my $rsp = $xml->createElement( "response" );    $xml->setDocumentElement( $rsp );
$rsp->appendTextChild( "result", $string );

# Try to forward the resulting XML to a receiver. Using STDOUT here, but files/sockets etc. yield the same results
# This will not warn and be encoded correctly but lack the XML header
print( "Just the root document looks good: '" . $xml->documentElement->serialize() . "'\n" );
# This will include the header but wide chars are mangled
print( $xml->serialize() );
# This will even issue a warning from encoding::warnings
print( "The full document looks mangled: '" . $xml->serialize() . "'\n" );
使用严格;使用诊断;使用“unicode_字符串”功能;
使用utf8;使用v5.14;使用编码::警告;
二进制模式(标准输出,“:编码(UTF-8)”;使用开放qw(:编码(UTF-8):标准);
使用XML::LibXML
#使用包含“?”的UTF-8编码文件模拟实际数据源

open(在“中),因为XML文档是在不需要任何外部信息的情况下解析的,所以它们是二进制文件而不是文本文件

您告诉Perl对发送到STDOUT[1]的任何内容进行编码,然后继续向其输出XML文档。您不能对二进制文件应用字符编码,因为它会损坏它

替换

binmode(STDOUT, ":encoding(UTF-8)");

注意:这假设输出的其余文本只是临时调试信息。否则,输出没有意义


  • 实际上,您可以这样做两次!一次使用
    使用openqw(:encoding(UTF-8):std);
    ,然后第二次使用
    binmode(STDOUT,“:encoding(UTF-8)”;

  • ikegami是正确的,但他没有真正解释问题所在。引用:

    重要提示:与其他节点的toString不同,在文档节点上,此函数以文档原始编码中的字节字符串形式返回XML(请参见actualEncoding()方法)

    serialize
    只是
    toString
    的别名)

    当您将字节字符串打印到标有
    :encoding
    层的文件句柄时,它会像ISO-8859-1一样进行编码。因为您有一个包含UTF-8字节的字符串,所以它会进行双重编码


    正如ikegami所说,使用
    binmode(STDOUT)
    从STDOUT中删除编码层。您也可以
    在打印前将
    序列化的结果解码成字符,但前提是文档使用的编码与您在输出文件句柄上设置的编码相同。(否则,您将发出一个实际编码与其标题声明不匹配的XML文档。)如果要打印到文件而不是标准输出,请使用
    '>:raw'
    打开它以避免双重编码。

    注意,
    使用openqw(:encoding(UTF-8):std);
    已经执行
    binmode(标准输出,:encoding(UTF-8)>)
    @cjm,添加了一个解释。这似乎很准确,是的。事实上,知道这一点并不会让我的生活变得太容易,因为现在我需要弄清楚什么时候将输出通道切换到原始模式,什么时候再将其反转。但问题已经得到了回答,为此我非常感谢你。你为什么要混合文本和binary文件?不要期望奇怪的东西比正常的东西更容易。你确定输出一个可能错误的头比不输出头更好吗?整个问题只涉及同一应用程序的各个模块之间的通信。传入数据转换为UTF-8,传出数据编码为任何请求,但内部对于每个人来说都是UTF-8,没有例外。玩代码的时候,我发现立即再次解码XML比摆弄输出编码更容易。这正是我在文档中错过的要点,谢谢。所以我在输出通道中混用了二进制和字符串对象,这当然不起作用。我会尝试相应地修复我的代码。感谢您提示多重编码不会有损,这样我就可以编码两次,并且只解码多余的编码运行而不会损坏我的输出。他没有建议您进行双重编码或者这样做不会有损。(取决于编码是否有损。)他只是说出现了双重编码。他确实建议您可以进行编码-解码-编码,这可能会导致XML文档损坏(取决于编码是否会损坏)
    binmode(STDOUT);