【问题标题】:Understanding lexical scoping of "use open ..." of Perl理解 Perl 的“use open ...”的词法范围
【发布时间】:2016-09-09 05:42:10
【问题描述】:
use open qw( :encoding(UTF-8) :std );

上述语句似乎仅在其词法范围内有效,不应影响其范围之外。但我观察到以下情况。

$ cat data
€
#1
$ perl -e '
    open (my $fh, "<encoding(UTF-8)", "data");  
    print($_) while <$fh>;'
Wide character in print at -e line 1, <$fh> line 1.
€

Wide character ... 警告在这里是完美的。但是

#2
$ perl
  my ($fh, $row);
  {
      use open qw( :encoding(UTF-8) :std );
      open ($fh, "<", "data");
  }
  $row = <$fh>;
  chomp($row);
  printf("%s (0x%X)", $row, ord($row));

  € (0x20AC)

不显示宽字符警告!这是imo这里发生的事情

  • 我们正在使用 open pragma 将 IO 流设置为 UTF-8,包括 STDOUT。
  • 在同一范围内打开文件。它将字符读取为多字节字符。
  • 但打印超出范围。打印语句应显示“宽字符”警告,但事实并非如此。为什么?

现在看下面,有点变化

#3
my ($fh, $row);
{
    use open qw( :encoding(UTF-8) :std );
}
open ($fh, "<", "data");
$row = <$fh>;
chomp($row);
printf("%s (0x%X)", $row, ord($row));

⬠(0xE2)

现在由于 open 语句超出了词法范围,open 以非 utf-8 模式打开了文件。

这是否意味着use open qw( :encoding(UTF-8) :std ); 语句在全局范围内更改了 STDOUT,但在词法范围内更改了 STDIN?

【问题讨论】:

  • 另见 Ricardo Signes 在 2016 年 YAPC NA 上的演讲 A Million Billion Squiggly Charactersopen pragma 的讨论始于 35:44
  • 是的,open qw(:std) 会全局更改标准句柄。正如里卡多在上述演讲中所讨论的那样。
  • :std 部分没有词法范围,仅更改 open() 使用的默认层,您甚至不会在 sn-ps 中使用。

标签: perl unicode utf-8 io


【解决方案1】:

您没有使用STDIN。您正在打开一个具有显式编码的文件(最后一个示例除外)并从中读取。

use open qw(:std ...) 影响 标准 文件句柄,但您只使用标准输出。当您不使用它并将 UTF-8 数据打印到标准输出时,您会收到警告。

在您的上一个示例中,您没有使用显式编码读取数据,因此当您将其打印到标准输出时,它已经损坏了。

这就是编码的诀窍,不管它们是什么。流程的每个部分都必须正确。

如果您希望use open 影响所有文件句柄,则必须以不同方式导入它。文档顶部有几个示例。

【讨论】:

  • 你是对的。我没有使用标准输入。但我仍然不清楚为什么我在 #2 中没有收到警告。 $row 应该包含一个多字节字符。但是 STDOUT 没有启用 UTF-8 标志。因此print 应该显示“宽字符...”警告。我在这里缺少哪一点?
  • @Samiron 也许 open 的 :std 部分不是词法范围的?
【解决方案2】:

不幸的是,open qw(:std) pragma 似乎不像词法 pragma,因为它在全局范围内更改了与标准句柄 STDINSTDOUTSTDERR 关联的 IO 层。由于use 语句发生在编译时,甚至源文件中较早的代码也会受到影响。所以以下

say join ":", PerlIO::get_layers(\*STDIN);
{
   use open qw( :encoding(UTF-8) :std );
}

打印(在我的 linux 平台上):

unix:perlio:encoding(utf-8-strict):utf8

而没有use open qw( :encoding(UTF-8) :std ) 它只会打印 unix:perlio.

一种不影响全局 STDOUT 的方法例如是在词法范围内复制句柄,然后将 IO 层添加到该范围内的复制句柄:

use feature qw(say);
use strict;
use warnings;
use utf8;

my $str = "€";
say join ":", PerlIO::get_layers(\*STDOUT);
{
    open ( my $out, '>&STDOUT' ) or die "Could not duplicate stdout: $!";
    binmode $out, ':encoding(UTF-8)';
    say $out $str;
}
say join ":", PerlIO::get_layers(\*STDOUT);
say $str;

带输出:

unix:perlio
€
unix:perlio
Wide character in say at ./p.pl line 16.
€

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    • 2012-07-27
    • 2023-04-01
    相关资源
    最近更新 更多