【问题标题】:Unicode string mess in perlperl中的Unicode字符串混乱
【发布时间】:2011-04-22 13:17:19
【问题描述】:

我有一个外部模块,它返回了一些字符串。我不确定字符串是如何返回的,确切地说。我真的不知道,Unicode 字符串是如何工作的以及为什么。

该模块应返回,例如,捷克语单词“být”,意思是“成为”。 (如果您看不到第二个字母 - 它应该看起来像 this。)如果我使用 Data Dumper 显示模块返回的字符串,我将其视为 b\x{fd}t

但是,如果我尝试使用print $s 打印它,我会收到“打印中的宽字符”警告,并且?而不是 ý。

如果我尝试Encode::decode(whatever, $s);,无论如何我都无法打印结果字符串(总是带有“宽字符”警告,有时带有错位字符,有时是正确的),无论我在whatever 中输入了什么。

如果我尝试Encode::encode("utf-8", $s);,则可以打印结果字符串而不会出现问题或错误消息。

如果我使用use encoding 'utf8';,则无需任何编码/解码即可打印。 但是,如果我使用IO::CaptureOutputCapture::Tiny 模块,它会再次开始喊“宽字符”。

我有几个问题,主要是关于到底发生了什么。 (我试图阅读 perldocs,但我不是很明智)

  1. 为什么我从模块中获取字符串后不能立即打印?
  2. 为什么我不能打印通过“decode”解码的字符串? “解码”究竟做了什么?
  3. “encode”究竟做了什么,为什么编码后打印没有问题?
  4. use encoding 到底是做什么的?为什么默认编码不同于utf-8
  5. 如果我想毫无问题地打印标量,即使我想使用其中一个捕获模块,我该怎么做?

编辑:有些人告诉我使用-CbinmodePERL_UNICODE。这是一个很好的建议。然而,不知何故,两个捕获模块都神奇地破坏了 STDOUT 的 UTF8 特性。这似乎更多是模块的错误,但我不太确定。

edit2:好的,最好的解决方案是转储模块并自己编写“捕获”(灵活性要小得多)。

【问题讨论】:

  • 你能展示一个演示问题的示例脚本吗?有很多事情要做,所以很容易从具体的事情开始。
  • 并且,当您拥有简短的示例脚本时,可以使用它向RT 上的那些模块报告错误。

标签: perl unicode utf-8


【解决方案1】:
  1. 因为您以 perl 的内部形式 (utf8) 将字符串输出到非 unicode 文件句柄。
  2. decode 函数将假定为 ENCODING 的字节序列解码为 Perl 的内部格式 (utf8)。您的输入似乎已被解码,
  3. encode() 函数将 Perl 内部形式的字符串编码为 ENCODING。
  4. encoding pragma 允许您以您喜欢的任何编码编写脚本。字符串文字会自动转换为 perl 的内部形式。
  5. 确保 perl 知道您的数据输入和输出的编码。

另见 perluniintro、perlunicode、编码模块、binmode() 函数。

【讨论】:

  • 非常感谢。如何打开 STDOUT 使其成为 unicode 文件句柄?为什么它不是默认的?编辑:哦,对了,我明白了,这是 binmode。第二个“子问题”仍然存在。为什么STDOUT的binmode默认不是UTF-8?
  • @Karel Bilek:由于向后兼容性问题,它可能不是默认设置。不过 Perl6 做到了这一点。
  • 不,很遗憾,它不起作用。捕获函数(两者)再次使 STDOUT 的 binmode 非 utf8,从而破坏它。
  • @Karel Bílek:因为当它是默认值时(仅在 5.8.0 中,并且仅当用户的语言环境是 utf8 时,IIRC)它为很多人破坏了很多东西。
【解决方案2】:

我推荐阅读我的书Effective Perl Programming中的Unicode章节。我们将所有能找到的文档放在一起,用 Perl 解释 Unicode 比我在其他任何地方看到的都更加连贯。

这个程序对我来说很好用:

#!perl

use utf8;
use 5.010;

binmode STDOUT, ':utf8';

my $string = return_string();

say $string;

sub return_string { 'být' }

另外,Capture::Tiny 对我来说很好用:

#!perl
use utf8;
use 5.010;
use Capture::Tiny qw(capture);

binmode STDOUT, ':utf8';

my( $stdout, $stderr ) = capture {
    system( $^X, '/Users/brian/Desktop/czech.pl' );
    };

say "STDOUT is [$stdout]";

IO::CaptureOutput 似乎有一些问题:

#!perl
use utf8;
use 5.010;
use IO::CaptureOutput qw(capture);

binmode STDOUT, ':utf8';

capture {
    system( $^X, '/Users/brian/Desktop/czech.pl' );
    } \my $stdout, \my $stderr;

say "STDOUT is [$stdout]";

为此我得到:

STDOUT is [být
]

不过,这很容易解决。不要使用该模块。 :)

【讨论】:

  • FWIW, IO::CaptureOutput 为我“工作”。但我认为那个错误和你得到的输出是正确的。 qx// 以我认为正确的方式工作,默认情况下为您捕获 5 个字符(包括换行符),指定 use open IN=>":utf8"; 时为您提供 4 个字符。
【解决方案3】:

您还应该查看PERL_UNICODE environment variable,这与使用the -C option 相同。这允许您将 STDIN/STDOUT/STDERR(和 @ARGV)设置为 UTF-8,而无需更改脚本。

【讨论】:

  • 不。即使-C 也无法在捕获功能中幸存下来。但似乎更多的是函数本身的问题,而不是 Perl。我猜。
  • 好的。最简单的解决方案......是自己编写函数(灵活性要小得多)。谢谢:)
  • @Karel Bílek,您对-C 使用了什么值?正如我链接到的文档中所述,有许多可能的设置。
猜你喜欢
  • 2014-12-03
  • 2011-11-26
  • 1970-01-01
  • 2015-05-06
  • 2020-06-28
  • 1970-01-01
  • 2014-02-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多