如何用钻石操作符(<>)读取UTF-8?

我想用Perl来读取UTF-8的input,不pipe它来自标准input还是来自文件,使用菱形运算符: while(<>){...}

所以我的脚本应该像以前一样用这两种方法调用,给出相同的结果:

 ./script.pl utf8.txt cat utf8.txt | ./script.pl 

但是输出不同! 只有第二个调用(使用cat )似乎按照devise工作,正确读取UTF-8。 这是脚本:

 #!/usr/bin/perl -w binmode STDIN, ':utf8'; binmode STDOUT, ':utf8'; while(<>){ my @chars = split //, $_; print "$_\n" foreach(@chars); } 

在两种情况下,我怎样才能正确读取UTF-8? 如果可能的话,我想继续使用钻石操作符<>来阅读。

编辑:

我意识到我可能应该描述不同的输出。 我的input文件包含这个序列: a\xCA\xA7b 。 用cat正确输出的方法是:

 a \xCA\xA7 b 

但另一种方法给了我这个:

 a \xC3\x8A \xC2\xA7 b 

尝试使用打开的杂注,而不是:

 use strict; use warnings; use open qw(:std :utf8); while(<>){ my @chars = split //, $_; print "$_" foreach(@chars); } 

你需要这样做,因为<>操作符是神奇的。 如您所知,它将从STDIN或@ARGV中的文件中读取。 从STDIN中读取不会导致STDIN已经打开,因此binmode可以正常工作。 问题是从@ARGV中读取文件时,当您的脚本启动并调用binmode文件未打开时。 这将导致STDIN被设置为UTF-8,但当@ARGV有文件时,不会使用此IO通道。 在这种情况下,<>运算符为@ARGV中的每个文件打开一个新的文件句柄。 每个文件句柄被重置,并失去它的UTF-8属性。 通过使用打开的编译指示,您可以强制每个新的STDIN使用UTF-8格式。

如果你这样做你的脚本:

 #!/usr/bin/perl -w binmode STDOUT, ':utf8'; while(<>){ binmode ARGV, ':utf8'; my @chars = split //, $_; print "$_\n" foreach(@chars); } 

<>读取的魔术文件句柄称为*ARGV ,当您调用readline时打开。

但是,真的,我是在适当的时候显式使用Encode::decodeEncode::encode的粉丝。

您可以使用-C标志在默认情况下打开UTF8:

 perl -CSD -ne 'print join("\n",split //);' utf8.txt 

交换机-CSD无条件打开UTF8; 如果只使用-C ,则只有在相关的环境variables( LC_ALLLC_TYPELANG )表示如此时才会打开UTF8。 有关详细信息,请参阅perlrun 。

如果您不直接调用perl(特别是,如果您将选项从shebang行传递给perl,则可能无法可靠地运行)。 在这种情况下查看其他答案。

如果你把一个binmode调用到while循环中,那么在第一行读入之后,它会把句柄切换到utf8模式。这可能不是你想要做的。

像下面的东西可能会更好地工作:

 #!/usr/bin/env perl -w binmode STDOUT, ':utf8'; eof() ? exit : binmode ARGV, ':utf8'; while( <> ) { my @chars = split //, $_; print "$_\n" foreach(@chars); } continue { binmode ARGV, ':utf8' if eof && !eof(); } 

使用parens调用eof()是不可思议的,因为它会检查<>使用的伪文件句柄上的文件结尾。 如有必要,它将打开需要读取的下一个句柄,这通常会使* ARGV有效,但不会读取任何内容。 这允许我们在从它读取任何内容之前对从第一个读取的文件进行binmode。

之后,使用eof(无parens) 这将检查从文件结尾读取的最后一个句柄。 在从命令行处理每个文件的最后一行(或者当stdin到达结尾时)之后,这将是真实的。

显然,如果我们刚刚处理了一个文件的最后一行,调用eof()(带有parens)将打开下一个文件(如果有的话),使* ARGV有效(如果可以),并testing文件结束在下一个文件。 如果下一个文件存在,并且不在文件结尾,那么我们可以安全地在ARGV上使用binmode。