使用哈希键写入XML文件时,使用XML::LibXML会出现编码错误

5

这个问题与这个问题相关:哈希键编码:为什么我使用Devel::Peek::Dump获得了两个不同的结果?
当我取消注释# utf8::upgrade( $name );行或者注释掉$hash{'müller'} = 'magenta';行时,它可以工作。

#!/usr/bin/env perl
use warnings;
use 5.014;
use utf8;
binmode STDOUT, ':encoding(utf-8)';
use XML::LibXML;

# Hash read in from a file:
# ... 
my %hash = ( 'müller' => 'green', 'schneider' => 'blue', 'bäcker' => 'red' );
# ...

# change or add something
$hash{'müller'} = 'magenta';

# writing Hash to xml file
my $doc = XML::LibXML::Document->new('1.0', 'UTF-8' );
my $root = $doc->createElement( 'my_test' );

for my $name ( keys %hash ) {
    # utf8::upgrade( $name );
    my $tag = $doc->createElement( 'item' );
    $tag->setAttribute( 'name' => $name );
    my $tag_color = $doc->createElement( 'color' );
    $tag_color->appendTextNode( $hash{$name} );
    $tag->appendChild( $tag_color );
    $root->appendChild( $tag );
}
$doc->setDocumentElement($root);
say $doc->serialize( 1 );
$doc->toFile( 'my_test.xml', 1 );

输出:

error : string is not in UTF-8  
encoding error : output conversion failed due to conv error, bytes 0xFC 0x6C 0x6C 0x65  
I/O error : encoder error  
<?xml version="1.0" encoding="ISO-8859-1"?>  
<my_test>  
  <item name="m    
i18n error : output conversion failed due to conv error, bytes 0xFC 0x6C 0x6C 0x65
I/O error : encoder error
2个回答

3
根据XML::LibXML文档,'müller' eq 'müller'是否为真取决于字符串的内部存储方式。这是一个错误,具体来说,将UTF8标志赋予含义称为“Unicode Bug”,XML::LibXML在this page的“编码支持”部分中记录了这一点。
这个Bug是已知的,但由于向后兼容性原因,无法进行彻底修复。Perl提供了两个工具来解决Unicode Bug的问题:
utf8::upgrade( $sv );    # Switch to the UTF8=1 storage format
utf8::downgrade( $sv );  # Switch to the UTF8=0 storage format

在这里使用前者将是适当的工具。

sub _up { my ($s) = @_; utf8::ugprade($s); $s }
$tag_color->appendTextNode( _up $hash{$name} );

注意:即使您没有使用use utf8;,也可以使用utf8::upgrade。只有在源代码为UTF-8时才使用use utf8;

这是正确的答案,不幸的是许多开发人员发现Perl对Unicode的实现难以理解或令人困惑(考虑到早期文档和部分当前文档,我不怪他们,Encode仍然暴露了不相关的内部存储)。 - chansen
@chansen,我认为造成混淆的最大因素是Perl本身在许多地方都受到了Unicode bug的影响。第二大因素是UTF8标志实际上是字符串中数据类型的一个非常好的指示器,即使这不是它的实际意义。 - ikegami

2

如果我将你的脚本保存为iso-8859-1格式,就会出现错误。但如果我将它保存为utf-8格式,就可以正常工作。


当我将源代码保存为UTF-8时,我得到与OP相同的结果。如果我将源代码保存为iso-8859-1(并删除“use utf8;”),除了相同的消息外,我还会收到一个额外的“error:string is not in UTF-8”的错误提示。 - ikegami

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接