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

标签 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(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:好案例:

<response><result>Value of '¿Üßıçñíïì' has no meaning</result></response>

剧透 2:糟糕的情况:

<?xml version="1.0" encoding="UTF-8"?><response><result>Value of '¿ÃÃıçñíïì' has no meaning</result></response>

根元素及其内容已经采用 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 ;)

最佳答案

ikegami 是正确的,但他并没有真正解释哪里出了问题。引用the docs for XML::LibXML::Document :

IMPORTANT: unlike toString for other nodes, on document nodes this function returns the XML as a byte string in the original encoding of the document (see the actualEncoding() method)!

(serialize只是toString的别名)

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

正如ikegami所说,使用binmode(STDOUT)从STDOUT中移除编码层。您还可以在打印之前将 serialize 的结果decode 回字符,但前提是文档使用的编码与您在输出文件句柄上设置的编码相同。 (否则,您将发出一个 XML 文档,其实际编码与其 header 声明的内容不匹配。)如果您打印到文件而不是 STDOUT,请使用 '>:raw' 打开它避免双重编码。

关于perl - 如何避免在 XML::LibXML 中使用双重 UTF-8 编码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21096900/

相关文章:

perl - Dancer Hooks 基于每个请求方法?

objective-c - libxml2 是否支持 XPath 2.0?

c++ - 在 libxml2 中使用 https 解析页面

regex - 从 map 中检索散列键

regex - Perl 正则表达式 : How to capture groups in piped text?

c - XS 模块和 perl 构建之间的结构定义冲突

visual-studio - 在 VisualStudio 中将所有 *.cs 文件转换为 unicode

java - Spring MVC UTF-8 字符编码

javascript - Javascript 无法正确读取 ASCII > 128 的字符

c++ - 如何随时停止使用 LIBXML SAX 解析 xml 文档?