perl中的Unicode字符串混乱

时间:2010-10-07 19:14:56

标签: perl unicode utf-8

我有一个外部模块,它会给我一些字符串。我不确定字符串是如何返回的。我真的不知道,Unicode字符串是如何工作的以及原因。

模块应该返回,例如捷克语“být”,意思是“将来”。 (如果你看不到第二个字母 - 它应该看起来像this。)如果我显示模块返回的字符串,使用数据转储器,我将其视为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. 为什么我不能打印字符串,通过“解码”解码?什么是“解码”呢?
  3. 究竟“编码”到底是什么,为什么编码后打印它没有问题?
  4. 究竟use encoding做了什么?为什么默认编码与utf-8不同?
  5. 即使我想使用其中一个捕获模块,如果我想毫无问题地打印标量,我该怎么做?
  6. 编辑:有人告诉我使用-CbinmodePERL_UNICODE。这是一个很好的建议。然而,不知何故,两个捕获模块都神奇地破坏了STDOUT的UTF8-ness。这似乎是模块的一个错误,但我不太确定。

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

3 个答案:

答案 0 :(得分:5)

  1. 因为您将perl的内部形式(utf8)中的字符串输出到非unicode文件句柄。
  2. decode函数将假定为ENCODING的字节序列解码为Perl的内部形式(utf8)。您的输入似乎已经解码,
  3. encode()函数将Perl内部表单中的字符串编码为ENCODING。
  4. encoding编译指示允许您以您喜欢的任何编码编写脚本。字符串文字自动转换为perl的内部形式。
  5. 确保perl知道您的数据进入哪个编码并出来。
  6. 另见perluniintro,perlunicode,Encode module,binmode()函数。

答案 1 :(得分:3)

我建议阅读我的书 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
]

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

答案 2 :(得分:1)

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