我的程序从数据源接收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" );
剧透1:好案例:
&lt; response&gt;&lt; result&gt;'¿Üßıçñíïì'的数值没有意义&lt; / result&gt;&lt; / response&gt;
剧透2:不好的案例:
&lt;?xml version =“1.0”encoding =“UTF-8”?&gt;&lt; response&gt;&lt; result&gt;'¿ÃñçñÃÃÃ'的价值没有意义&lt; /导致&GT;&LT; /响应&GT;
根元素及其内容已经是UTF-8编码的。 XML :: LibXML接受输入并能够处理它并再次输出为有效的UTF-8。一旦我尝试序列化整个XML文档,内部的宽字符就会被破坏。在十六进制转储中,看起来好像已经UTF-8编码的字符串再次通过UTF-8编码器。我已经搜索,尝试和阅读了很多内容,从Perl's own Unicode tutorial一直到tchrist's对Why does modern Perl avoid UTF-8 by default?问题的极好回答。我不认为这是一般的Unicode问题,而是我和XML :: LibXML之间的特定问题。
如果能够输出包含标题的完整XML文档以使其内容保持正确编码,我需要做什么?是否有要设置的标志/属性/开关?
(我很乐意接受指向 TFM 的相应部分的链接,只要它们确实有用,我应该 R ;)
答案 0 :(得分:4)
重要提示:与其他节点的toString不同,在文档节点上,此函数将XML作为文档原始编码中的字节字符串返回(请参阅actualEncoding()方法)!
(serialize
只是toString
)的别名
当您将字节字符串打印到标有:encoding
图层的文件句柄时,它会被编码为ISO-8859-1。由于您有一个包含UTF-8字节的字符串,因此会进行双重编码。
正如ikegami所说,使用binmode(STDOUT)
从STDOUT中删除编码层。在打印之前,您还可以decode
将serialize
的结果重新转换为字符,但这假定文档使用的是您在输出文件句柄上设置的相同编码。 (否则,您将发出一个XML文档,其实际编码与其标题声明的内容不匹配。)如果您要打印到文件而不是STDOUT,请使用'>:raw'
打开它以避免双重编码。
答案 1 :(得分:3)
由于XML文档的解析不需要任何外部信息,因此它们是二进制文件而不是文本文件。
您告诉Perl编码发送到STDOUT [1] 的任何内容,但随后您继续输出XML文档。您不能将字符编码应用于二进制文件,因为它会破坏它。
替换
binmode(STDOUT, ":encoding(UTF-8)");
与
binmode(STDOUT);
注意:这假定您输出的其余文本只是临时调试信息。输出没有其他意义。
use open qw( :encoding(UTF-8) :std );
后,再使用binmode(STDOUT, ":encoding(UTF-8)");
。