为什么我必须在Perl裸字文件句柄之前使用*? [英] Why do I have to use a * in front of a Perl bareword filehandle?

查看:90
本文介绍了为什么我必须在Perl裸字文件句柄之前使用*?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

尝试这样做时:

 my $obj = new JavaScript::Minifier;
 $obj->minify(*STDIN, *STDOUT);
// modified above line to
 $obj->minify(*IP_HANDLE,*OP_HANDLE)

如果IP_HANDLE和OP_HANDLE是文件句柄,则上述方法适用,但仍然无法弄清楚*在应用于文件句柄或任何其他数据类型时的实际作用.

The above works if IP_HANDLE and OP_HANDLE are filehandles but still I am not able to figure out what actually the * does when applied to a filehandle or any other datatype.

谢谢

推荐答案

在perl v5.6之前的糟糕年代,引入了词法文件句柄-超过了

In the bad old days before perl v5.6, which introduced lexical filehandles — more than a decade ago now — passing file- and directory handles was awkward. The code from your question is written using this old-fashioned style.

例如*STDIN的技术名称是 typeglob ,在.您可能会在传统代码中出于各种目的而遇到对typeglob的操纵.请注意,您只能获取全局变量的typeglob,而不能获取词法.

The technical name for *STDIN, for example, is a typeglob, explained in the "Typeglobs and Filehandles" section of perldata. You may encounter manipulation of typeglobs for various purposes in legacy code. Note that you may grab typeglobs of global variables only, never lexicals.

传递句柄是直接处理typeglob的常见目的,但是还有其他用途.有关详情,请参见下文.

Passing handles was a common purpose for dealing directly with typeglobs, but there were other uses as well. See below for details.

  • 将文件句柄传递给subs
  • 语法歧义:字符串或文件句柄
  • 通过typeglob分配进行别名
  • 通过本地化typeglob来本地化句柄
  • 内幕偷窥:*foo{THING}语法
  • 一起解决:DWIM!
  • Passing filehandles to subs
  • Syntactic ambiguity: string or filehandle
  • Aliases via typeglob assignment
  • Localizing handles by localizing typeglobs
  • Peeking under the hood: *foo{THING} syntax
  • Tying it all together: DWIM!

perldata文档说明了:

Typeglob和文件句柄

Perl使用称为 typeglob 的内部类型来保存整个符号表条目. typeglob的类型前缀为*,因为它表示所有类型.这曾经是通过引用将数组和散列传递给函数的首选方法,但是现在我们有了真正的引用,因此几乎不需要.

Typeglobs and Filehandles

Perl uses an internal type called a typeglob to hold an entire symbol table entry. The type prefix of a typeglob is a * because it represents all types. This used to be the preferred way to pass arrays and hashes by reference into a function, but now that we have real references, this is seldom needed.

[...]

typeglobs的另一个用途是将文件句柄传递到函数中或创建新的文件句柄.如果您需要使用typeglob来保存文件句柄,请按照以下方式进行操作:

Another use for typeglobs is to pass filehandles into a function or to create new filehandles. If you need to use a typeglob to save away a filehandle, do it this way:

$fh = *STDOUT;

或作为真正的参考,例如:

or perhaps as a real reference, like this:

$fh = \*STDOUT;

有关以下示例,请参见 perlsub 使用这些作为函数中的间接文件句柄.

See perlsub for examples of using these as indirect filehandles in functions.

perlsub的引用部分在下面.

传递符号表条目(类型标记)

警告:本节中描述的机制最初是在较旧版本的Perl中模拟按引用传递的唯一方法.尽管它在现代版本中仍然可以正常工作,但通常更易于使用新的引用机制.见下文.

Passing Symbol Table Entries (typeglobs)

WARNING: The mechanism described in this section was originally the only way to simulate pass-by-reference in older versions of Perl. While it still works fine in modern versions, the new reference mechanism is generally easier to work with. See below.

有时您不希望将数组的值传递给子例程,而希望将其传递给子例程,以便子例程可以修改其全局副本而不是使用本地副本.在Perl中,可以通过在名称前面加上星号来引用特定名称的所有对象:*foo.通常将其称为"typeglob",因为可以将前面的星星视为变量和子例程等上所有有趣的前缀字符的通配符匹配.

Sometimes you don’t want to pass the value of an array to a subroutine but rather the name of it, so that the subroutine can modify the global copy of it rather than working with a local copy. In Perl you can refer to all objects of a particular name by prefixing the name with a star: *foo. This is often known as a "typeglob," because the star on the front can be thought of as a wildcard match for all the funny prefix characters on variables and subroutines and such.

在求值时,typeglob产生一个标量值,代表该名称的所有对象,包括任何文件句柄,格式或子例程.分配给它时,它使提到的名称引用分配给它的任何*值. [...]

When evaluated, the typeglob produces a scalar value that represents all the objects of that name, including any filehandle, format, or subroutine. When assigned to, it causes the name mentioned to refer to whatever * value was assigned to it. [...]

请注意,typeglob只能用于全局变量,而不能用于词法.注意上面的警告.最好避免使用这种晦涩的技巧.

Note that a typeglob can be taken on global variables only, not lexicals. Heed the warning above. Prefer to avoid this obscure technique.

没有*标记,裸字就是一个字符串.

Without the * sigil, a bareword is just a string.

简单的字符串有时就足够了.例如, print 运算符允许

Simple strings sometimes suffice, hower. For example, the print operator allows

$ perl -le 'print { "STDOUT" } "Hiya!"'
Hiya!

$ perl -le '$h="STDOUT"; print $h "Hiya!"'
Hiya!

$ perl -le 'print "STDOUT" +123'
123

这些失败,并启用了strict 'refs'.手册说明:

These fail with strict 'refs' enabled. The manual explains:

FILEHANDLE可以是标量变量名称,在这种情况下,该变量包含文件句柄的名称或对文件句柄的引用,从而引入了一个间接级别.

FILEHANDLE may be a scalar variable name, in which case the variable contains the name of or a reference to the filehandle, thus introducing one level of indirection.

在您的示例中,请考虑句法歧义.没有*标记,您可能是指字符串

In your example, consider the syntactic ambiguity. Without the * sigil, you could mean strings

$ perl -MO=Deparse,-p prog.pl
use JavaScript::Minifier;
(my $obj = 'JavaScript::Minifier'->new);
$obj->minify('IP_HANDLE', 'OP_HANDLE');

或者可能是子通话

$ perl -MO=Deparse,-p prog.pl
use JavaScript::Minifier;
sub OP_HANDLE {
    1;
}
(my $obj = 'JavaScript::Minifier'->new);
$obj->minify('IP_HANDLE', OP_HANDLE());

,或者当然是文件句柄.请注意,在上面的示例中,裸字JavaScript::Minifier也如何编译为简单的字符串.

or, of course, a filehandle. Note in the examples above how the bareword JavaScript::Minifier also compiles as a simple string.

启用strict编译指示,无论如何一切都会消失在窗口中:

Enable the strict pragma and it all goes out the window anyway:

$ perl -Mstrict prog.pl
Bareword "IP_HANDLE" not allowed while "strict subs" in use at prog.pl line 6.
Bareword "OP_HANDLE" not allowed while "strict subs" in use at prog.pl line 6.

通过typeglob分配进行别名

对于堆栈溢出帖子来说,使用typeglob的一个技巧很方便

Aliases via typeglob assignment

One trick with typeglobs that’s handy for Stack Overflow posts is

*ARGV = *DATA;

(我可能会更精确地使用*ARGV = *DATA{IO},但这有点麻烦.)

(I could be more precise with *ARGV = *DATA{IO}, but that’s a little fussy.)

这允许菱形运算符<>DATA文件句柄读取,如

This allows the diamond operator <> to read from the DATA filehandle, as in

#! /usr/bin/perl

*ARGV = *DATA;   # for demo only; remove in production

while (<>) { print }

__DATA__
Hello
there

这样,程序及其输入可以在一个文件中,并且代码与其在生产环境中的外观更匹配:只需删除typeglob赋值即可.

This way, the program and its input can be in a single file, and the code is a closer match to how it will look in production: just delete the typeglob assignment.

在perlsub中注意到

As noted in perlsub

通过local()

的临时值

警告:通常,您应该使用my而不是local,因为它更快,更安全.例外包括全局标点符号变量,全局文件句柄和格式以及对Perl符号表本身的直接操作. local通常用于当变量的当前值必须对被调用的子例程可见时使用. [...]

Temporary Values via local()

WARNING: In general, you should be using my instead of local, because it’s faster and safer. Exceptions to this include the global punctuation variables, global filehandles and formats, and direct manipulation of the Perl symbol table itself. local is mostly used when the current value of a variable must be visible to called subroutines. [...]

您可以使用typeglobs本地化文件句柄:

you can use typeglobs to localize filehandles:

$ cat prog.pl
#! /usr/bin/perl

sub foo {
  local(*STDOUT);
  open STDOUT, ">", "/dev/null" or die "$0: open: $!";
  print "You can't see me!\n";
}

print "Hello\n";
foo;
print "Good bye.\n";

$ ./prog.pl
Hello
Good bye.

何时仍要使用local() "中的"还有另一个例子.

"When to Still Use local()" in perlsub has another example.

2..您需要创建本地文件或目录句柄或本地函数.

2. You need to create a local file or directory handle or a local function.

需要自己的文件句柄的函数必须在完整的typeglob上使用local().这可以用来创建新的符号表条目:

A function that needs a filehandle of its own must use local() on a complete typeglob. This can be used to create new symbol table entries:

sub ioqueue {
    local (*READER, *WRITER); # not my!
    pipe (READER, WRITER) or die "pipe: $!";
    return (*READER, *WRITER);
}
($head, $tail) = ioqueue();

要强调的是,这种风格是过时的.最好避免在新代码中使用全局文件句柄,但是能够理解现有代码中的技术很有用.

To emphasize, this style is old-fashioned. Prefer to avoid global filehandles in new code, but being able to understand the technique in existing code is useful.

您可以进入类型组的不同部分,如 perlref 解释:

You can get at the different parts of a typeglob, as perlref explains:

可以使用一种特殊的语法创建引用,该语法被人们称为*foo{THING}语法. *foo{THING}返回对*foo中THING插槽的引用(这是保存所有称为foo的符号表条目).

A reference can be created by using a special syntax, lovingly known as the *foo{THING} syntax. *foo{THING} returns a reference to the THING slot in *foo (which is the symbol table entry which holds everything known as foo).

$scalarref = *foo{SCALAR};
$arrayref = *ARGV{ARRAY};
$hashref = *ENV{HASH};
$coderef = *handler{CODE};
$ioref = *STDIN{IO};
$globref = *foo{GLOB};
$formatref = *foo{FORMAT};

除了*foo{IO},所有这些都是不言自明的.它返回IO句柄,用于文件句柄(open),套接字(socketsocketpair)和目录句柄(opendir).为了与以前的Perl版本兼容,*foo{FILEHANDLE}*foo{IO}的同义词,尽管从5.8.0开始不推荐使用.如果弃用警告有效,则会警告其使用.

All of these are self-explanatory except for *foo{IO}. It returns the IO handle, used for file handles (open), sockets (socket and socketpair), and directory handles (opendir). For compatibility with previous versions of Perl, *foo{FILEHANDLE} is a synonym for *foo{IO}, though it is deprecated as of 5.8.0. If deprecation warnings are in effect, it will warn of its use.

*foo{THING}返回undef(标量除外).如果尚未使用$foo,则*foo{SCALAR}返回对匿名标量的引用.将来的版本中可能会更改.

*foo{THING} returns undef if that particular THING hasn’t been used yet, except in the case of scalars. *foo{SCALAR} returns a reference to an anonymous scalar if $foo hasn’t been used yet. This might change in a future release.

*foo{IO}是[perldata中的"Typeglobs和Filehandles"]中给出的*HANDLE机制的替代方法,用于将文件句柄传入或传出子例程,或存储到更大的数据结构中.它的缺点是不会为您创建新的文件句柄.它的优点是,与分配typeglob分配相比,您遭受破坏的风险更低. (不过,它仍然可以压缩文件和目录句柄.)但是,如果像下面的示例中那样将传入值分配给标量而不是typeglob,则不会发生这种情况.

*foo{IO} is an alternative to the *HANDLE mechanism given in ["Typeglobs and Filehandles" in perldata] for passing filehandles into or out of subroutines, or storing into larger data structures. Its disadvantage is that it won’t create a new filehandle for you. Its advantage is that you have less risk of clobbering more than you want to with a typeglob assignment. (It still conflates file and directory handles, though.) However, if you assign the incoming value to a scalar instead of a typeglob as we do in the examples below, there’s no risk of that happening.

splutter(*STDOUT); # pass the whole glob
splutter(*STDOUT{IO}); # pass both file and dir handles

sub splutter {
  my $fh = shift;
  print $fh "her um well a hmmm\n";
}

$rec = get_rec(*STDIN); # pass the whole glob
$rec = get_rec(*STDIN{IO}); # pass both file and dir handles

sub get_rec {
  my $fh = shift;
  return scalar <$fh>;
}

一起解决:DWIM!

上下文是Perl的关键.在您的示例中,尽管语法可能模棱两可,但其意图不是:即使参数是字符串,这些字符串也显然是用来命名文件句柄的.

Tying it all together: DWIM!

Context is key with Perl. In your example, although the syntax may be ambiguous, the intent is not: even if the parameters are strings, those strings are clearly intended to name filehandles.

因此,请考虑minify可能需要处理的所有情况:

So consider all the cases minify may need to handle:

  • 流行语
  • 裸机类型
  • 对typeglob的引用
  • 标量中的文件句柄

例如:

#! /usr/bin/perl

use warnings;
use strict;

*IP_HANDLE = *DATA;
open OP_HANDLE, ">&STDOUT";
open my $fh, ">&STDOUT";
my $offset = tell DATA;

use JavaScript::Minifier;
my $obj = JavaScript::Minifier->new;
$obj->minify(*IP_HANDLE, "OP_HANDLE");

seek DATA, $offset, 0 or die "$0: seek: $!";
$obj->minify(\*IP_HANDLE, $fh);

__DATA__
Ahoy there
matey!

作为图书馆作者,包容性可能会有用.为了说明这一点,下面的JavaScript :: Minifier存根理解了老式和现代的传递文件句柄的方式.

As a library author, being accomodative can be useful. To illustrate, the following stub of JavaScript::Minifier understands both old-fashioned and modern ways of passing filehandles.

package JavaScript::Minifier;

use warnings;
use strict;

sub new { bless {} => shift }

sub minify {
  my($self,$in,$out) = @_;

  for ($in, $out) {
    no strict 'refs';
    next if ref($_) || ref(\$_) eq "GLOB";

    my $pkg = caller;
    $_ = *{ $pkg . "::" . $_ }{IO};
  }

  while (<$in>) { print $out $_ }
}

1;

输出:

$ ./prog.pl
Name "main::OP_HANDLE" used only once: possible typo at ./prog.pl line 7.
Ahoy there
matey!
Ahoy there
matey!

这篇关于为什么我必须在Perl裸字文件句柄之前使用*?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆