【问题标题】:Perl XML::Twig character encodingPerl XML::Twig 字符编码
【发布时间】:2015-06-04 18:49:16
【问题描述】:

我有一组 XML 文件,其中包含非简单 ASCII 字符和编码字符的组合,例如:

... many 8-bit characters such as é, ⪚, and ñ.

(第二个字符是与分号版本的⪚。第一个和第三个是未转义的字符。)

文件为 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 编码:

open(NEW,'>:encoding(UTF-8)', $outfile);

它变得更加奇怪:

...such as Ã?©, ⪚, and Ã?±. 

知道如何毫发无损地通过角色和实体吗? 谢谢你, 斯科特

【问题讨论】:

  • 您误解了编码和转义的工作原理。 é 是 Unicode U+00E9⪚ 是 Unicode U+2A9Añ 是 Unicode U+00F1。没有像 非简单 ASCII 字符 这样的东西,而且您的任何字符都没有被转义。目前尚不清楚等于或大于是否在您的文件中显示为⪚;请澄清
  • 嗨,是的,对于混淆的术语,我很抱歉,我不熟悉它是如何工作或讨论的。文件中出现的等号或更大的符号是⪚,也就是和号-xxx-分号的版本。重音-e 和波浪号-n 是文件中的那些确切字符。谢谢。

标签: xml perl encoding utf-8


【解决方案1】:

第一件事:在您的情况下,keep_encoding 应该使用。这是一个古老的选择,可以追溯到远古时代,当时 latin1 是一种常用的编码,而 perl 对 unicode 不太好。我在这里说的是 5.8 之前的版本。该选项为生活在全拉丁世界中的人们提供了一种无需处理 unicode 即可处理 XML 的方法。将它与 utf-8 数据一起使用会导致疯狂(以及您发现的编码问题)。

正如其他答案中提到的,输出文件需要以utf8 模式打开,无论是在open 还是通过use utf8::all;。这消除了wide character 警告,并避免了更糟糕的情况,即如果输出仅包含 ascii 和扩展的 ascii 字符(perl 这样做是为了保持向后兼容性,您可以在删除 @987654326 时看到它) @ 来自您的输入)。

完成此操作后,输出文件将采用正确的 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 是一种 hack。如果您确实需要将扩展​​ ascii 字符保留为字符并将其他字符编码为数字字符实体,您将向output_filter 提供一个自定义函数,它应该接收字符串(所有 utf-8 字符),并将字符串返回给输出(将一些字符编码为数字实体)

也就是说,我不确定您是否需要忠实于原始格式。 XML 处理器不应该关心它。事实上,这就是为什么很难保持编码的原因:调用解析器的代码只将文本视为 utf-8 字符串,所有实体都已解码。

【讨论】:

  • 那行得通,我认为关键是output_filter => 'safe',,这是我之前遗漏的部分。非常感谢!! - 斯科特
  • 当然,使用output_filter => 'safe' 将所有内容转换为实体,我可以接受,但如果有人需要将重音字符保留在其中,这似乎保留了那些但会破坏实体版本(第二个字符)。
【解决方案2】:

除了确保将输入和输出 IO 通道设置为 UTF-8 编码之外,您无需执行任何特殊操作。 Wide character in print 警告表明您正在尝试将宽字符(大于 255 的代码点)打印到仅具有字节语义的通道

如果我使用这些数据

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <text>... many 8-bit characters such as é, &#10906;, and ñ.</text>
</root>

下面的代码一切正常。关键是use open qw/ :std :encoding(utf-8) /,它将 STDIN、STDOUT 和 STDERR 以及任何其他新打开的文件句柄设置为使用 UTF-8 编码

不幸的是,keep_encoding 选项似乎同时控制实体扩展和输出编码,我看不出有办法说服XML::Twig 在启用时返回一个简单的字符串,而你只能得到一个编码的字节序列,您必须调用 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 é, &#10906;, and ñ.

更新

这是为了解释你得到的输出

如果我添加 keep_encoding => 1(上面已注释掉),实体会被保留,> 但第一个和第三个字符会损坏:

...例如 é、⪚ 和 ñ。

这些字符没有损坏,文本被输出为 UTF-8,但无论您使用什么查看它都需要一个字节编码,例如 ISO-8859-1。当编码为 UTF-8 时,e-acute 字符 U+00E9 是一个两字节字符 0xC3 0xA9。当解释为 ISO-8859-1 时,0xC3 是 A-波浪号,0xA9 是版权标志,这正是您所看到的。如果您使用预期 UTF-8 编码数据的东西,那么您将看到单个字符 e-acute

如果我在刷新中添加 UTF-8 编码:

open(NEW,'>:encoding(UTF-8)', $outfile);

它变得更加奇怪:

...例如 Ã?©、⪚ 和 Ã?±。

这里发生的情况是,虽然来自 XML::Twig 的字符串已经编码为 UTF-8,但数据并未标记为如此。这意味着构成 UTF-8 编码字符的两个字节被视为单独的字符,它们被编码再次总共给出四个字符

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-20
    • 1970-01-01
    • 2010-12-14
    • 2016-11-20
    • 2013-06-17
    • 1970-01-01
    相关资源
    最近更新 更多