当我使用 open ':std', ':encoding(UTF-8)'; 时,为什么 File::Slurp 得到 UTF8 字符错误?

Rob*_*ert 2 perl utf-8

我在Ubuntu一个Perl 5.30.0节目里的组合File::Slurp,并open ':std', ':encoding(UTF-8)'没有得到正确读取UTF8结果:

use strict;
use warnings;
use open ':std', ':encoding(UTF-8)';
use File::Slurp;

my $text = File::Slurp::slurp('input.txt');
print "$text\n";
Run Code Online (Sandbox Code Playgroud)

“input.txt”是一个带有此内容的 UTF8 编码文本文件(无 BOM):

ö
Run Code Online (Sandbox Code Playgroud)

当我运行它时,ö显示为ö. 只有当我删除该use open...行时,它才能按预期工作并且ö打印为ö.

当我手动读取如下文件时,一切都按预期工作,我确实得到了ö

$text = '';
open my $F, '<', "input.txt" or die "Cannot open file: $!";
while (<$F>) {
    $text .= $_;
}
close $F;
print "$text\n";
Run Code Online (Sandbox Code Playgroud)

为什么会这样,去这里的最佳方式是什么?在open编译过时还是我失去了什么东西?

ike*_*ami 5

与许多编译指示一样,[1]的效果use open是词法范围的。[2]这意味着它只影响找到它的块或文件的其余部分。这样的 pragma 不会影响其作用域之外的函数中的代码,即使它们是从其作用域调用的。

您需要将解码流的愿望传达给 File::Slurp。这不能使用 来完成slurp,但可以read_file通过它的binmode参数来完成。

use open ':std', ':encoding(UTF-8)';  # Still want for effect on STDOUT.
use File::Slurp qw( read_file );

my $text = read_file('input.txt', { binmode => ':encoding(UTF-8)' });
Run Code Online (Sandbox Code Playgroud)

更好的模块是File::Slurper

use open ':std', ':encoding(UTF-8)';  # Still want for effect on STDOUT.
use File::Slurper qw( read_text );

my $text = read_text('input.txt');
Run Code Online (Sandbox Code Playgroud)

File::Slurperread_text默认使用 UTF-8 解码。


没有模块,你可以使用

use open ':std', ':encoding(UTF-8)';

my $text = do {
   my $qfn = "input.txt";
   open(my $F, '<', $qfn)
      or die("Can't open file \"$file\": $!\n");
   local $/;
   <$fh>
};
Run Code Online (Sandbox Code Playgroud)

当然,这不像早期的解决方案那么清楚。


  1. 其他著名的例子包括use VERSIONuse strictuse warningsuse featureuse utf8
  2. 对 STDIN、STDOUT 和 STDERR from 的影响:std是全局的。