我有一组XML文件,其中包含非简单ASCII字符和编码字符的组合,例如:
... many 8-bit characters such as é, ⪚, and ñ.
(第二个字符是amp的&符号分号。第一个和第三个是非转义字符。)
文件采用UTF-8格式。
当我使用XML :: Twig运行我的Perl脚本时,实体(上面的第二个字符)变成一个未知字符(我在打印文件时得到'宽字符'消息)
这是我的代码。所有处理程序正在读取XML,而不是进行任何更改:
my $twig= XML::Twig->new(
comments => 'keep',
output_encoding => 'UTF-8',
# keep_encoding => 1,
twig_handlers => { topicref => \&topicref_processing,
xref => \&topicref_processing,
link => \&topicref_processing},
pretty_print => 'indented',
);
$twig->parsefile($file);
my($outfile) = $file;
$outfile =~ s/([.]dita)/.out$1/i;
open(NEW,">$outfile");
$twig->flush( \*NEW);
close(NEW);
如果我添加keep_encoding => 1(上面已注释),实体被保留,但第一个和第三个字符被破坏:
...such as é, ⪚, and ñ.
如果我将UTF-8编码添加到flush:
open(NEW,'>:encoding(UTF-8)', $outfile);
它变得更奇怪了:
...such as Ã?©, ⪚, and Ã?±.
关于如何通过未受损害的角色和实体传递任何想法? 谢谢, 斯科特
答案 0 :(得分:2)
除了确保输入和输出IO通道设置为UTF-8编码外,您无需做任何特殊操作。 Wide character in print
警告表示您正在尝试将宽字符(大于255的代码点)打印到仅具有字节语义的通道
如果我使用此数据
<?xml version="1.0" encoding="UTF-8"?>
<root>
<text>... many 8-bit characters such as é, ⪚, and ñ.</text>
</root>
下面的代码一切正常。关键是use open qw/ :std :encoding(utf-8) /
设置STDIN,STDOUT和STDERR,以及任何其他新打开的文件句柄,以使用UTF-8编码
不幸的是,keep_encoding
选项似乎控制了实体扩展和输出编码,我看不到一种方法来说服XML::Twig
在启用时返回一个简单的字符串,而且你所有的can get是一个编码的字节序列,在将其传递给编码输出通道之前,必须先调用decode_utf8
来获取字符。如果有人知道更好的方法来处理这个,那么我将不胜感激。当然,可以将模块中的编码数据发送到:raw
输出通道,但这不是事情应该起作用的方式
请注意,要查看输出中的字符⪚
,您必须使用具有该代码点字形的字体。大多数字体都没有该字符
use strict;
use warnings;
use open qw/ :std :encoding(utf-8) /;
use XML::Twig ();
use Encode qw/ decode_utf8 /;
my $twig = XML::Twig->new( keep_encoding => 1 );
$twig->parsefile('utf-8.xml');
my ($text) = $twig->findnodes('/root/text');
$text = decode_utf8($text->trimmed_text);
print $text, "\n";
<强>输出强>
... many 8-bit characters such as é, ⪚, and ñ.
<强>更新强>
这是为了解释你得到的输出
如果我添加keep_encoding =&gt; 1(上面已注释),实体被保留,&gt;但是第一个和第三个字符被破坏了:
...例如Ã,,⪚和Ã。。
这些字符不是损坏的,文本输出为UTF-8,但无论你使用什么来查看它都需要字节编码,类似于ISO-8859-1。编码为UTF-8时的e-acute字符U+00E9
是一个双字节字符0xC3 0xA9
。当解释为ISO-8859-1时,0xC3是A-tilde,而0xA9是版权符号,这正是您所看到的。如果您使用的是期望UTF-8编码的数据,那么您将看到单个字符e-acute而不是
如果我将UTF-8编码添加到flush:
open(NEW,'&gt;:encoding(UTF-8)',$ outfile);
它变得更奇怪了:...例如é,⪚和Ã?±。
这里发生的是,虽然来自XML::Twig
的字符串已编码为UTF-8,但数据未标记为如此。这意味着形成UTF-8编码字符的两个字节被视为单个字符,并且它们再次编码共给出四个字符
答案 1 :(得分:2)
首先:在你的情况下,keep_encoding
应该不。这是一个古老的选择,可以追溯到远古时代,当latin1是一种常用的编码时,perl对于unicode来说并不是那么好。我在这里说5.8前。该选项为生活在全拉丁世界的人们提供了一种处理XML的方法,而无需处理unicode。使用utf-8数据会导致疯狂(以及您发现的编码问题)。
如其他答案中所述,输出文件需要在utf8
模式下打开,可以在open
或use utf8::all;
中打开。这消除了wide character
警告,并避免更糟糕的情况,如果输出只包含ascii和扩展的ascii字符,输出将转换为latin1(perl这样做是为了保持向后兼容性,如果你删除了它,你可以看到它来自您输入的⪚
。
完成此操作后,输出文件将处于正确的utf-8状态,未转义。如果显示不正确,可能是您的终端不支持utf-8。
如果您需要转义所有非ascii字符,可以使用output_filter => 'safe'
选项,如下面的代码所示。
#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig;
use utf8::all; # either this or open the output file with '>:utf8'
my $file= 'test_enc.dita';
my $twig= XML::Twig->new(
comments => 'keep',
# escapes all non-ascii characters (including accented ones)
output_filter => 'safe',
twig_handlers => { topicref => \&topicref_processing,
xref => \&topicref_processing,
link => \&topicref_processing},
pretty_print => 'indented',
);
$twig->parsefile( $file);
my($outfile) = $file;
$outfile =~ s/([.]dita)/.out$1/i;
# current best practices recommend the use the 3 args form of
# open and lexical filehandles
open( my $out,'>', $outfile);
$twig->flush( $out);
close( $out);
没有真正的方法来忠实地保存编码/非编码形式的字符,除了keep_encoding这是一个黑客。如果你真的需要将extended-ascii字符保存为字符并将其他字符编码为数字字符实体,那么你将为output_filter
提供自定义函数,它应该接收字符串(所有utf-8字符),并返回要输出的字符串(某些字符编码为数字实体)
那说我不确定你是否需要忠实于原始格式。 XML处理器不应该关心它。事实上,这就是为什么很难保持编码:调用解析器的代码只将文本视为utf-8字符串,所有实体都已经解码。