Perl符号表中的不可打印字符代表什么?

10
我刚刚了解到,在Perl中,给定模块的符号表存储在与模块名称匹配的哈希表中--因此,例如,虚构模块Foo::Bar的符号表将是%Foo :: Bar。默认符号表存储在%main::中。仅出于好奇心,我决定想看看%main::里面有什么内容,所以遍历了哈希表中的每个键/值对,并将它们打印出来:
#! /usr/bin/perl

use v5.14;
use strict;
use warnings;

my $foo;
my $bar;
my %hash;

while( my ( $key, $value ) = each  %:: )  {
    say "Key: '$key' Value '$value'";
} 

输出结果如下:
Key: 'version::' Value '*main::version::'
Key: '/' Value '*main::/'
Key: '' Value '*main::'
Key: 'stderr' Value '*main::stderr'
Key: '_<perl.c' Value '*main::_<perl.c'
Key: ',' Value '*main::,'
Key: '2' Value '*main::2'
...

我原本期望能看到STDOUT和STDERR文件句柄,以及@INC和%ENV… 但我没想到还会出现非ASCII字符… 上面的代码块没有展示的是,输出的第三行实际上有一个表示不可打印字符的字形。

我运行了脚本,并进行了如下的管道操作:

perl /tmp/asdf.pl | grep '[^[:print:]]' | while read line
do 
    echo $line
    od -c <<< $line
    echo
done

输出结果如下:
Key: '' Value '*main::'
0000000   K   e   y   :       ' 026   '       V   a   l   u   e       '
0000020   *   m   a   i   n   :   : 026   '  \n
0000032

Key: 'ARNING_BITS' Value '*main::ARNING_BITS'
0000000   K   e   y   :       ' 027   A   R   N   I   N   G   _   B   I
0000020   T   S   '       V   a   l   u   e       '   *   m   a   i   n
0000040   :   : 027   A   R   N   I   N   G   _   B   I   T   S   '  \n
0000060

Key: '' Value '*main::'
0000000   K   e   y   :       ' 022   '       V   a   l   u   e       '
0000020   *   m   a   i   n   :   : 022   '  \n
0000032

Key: 'E_TRIE_MAXBUF' Value '*main::E_TRIE_MAXBUF'
0000000   K   e   y   :       ' 022   E   _   T   R   I   E   _   M   A
0000020   X   B   U   F   '       V   a   l   u   e       '   *   m   a
0000040   i   n   :   : 022   E   _   T   R   I   E   _   M   A   X   B
0000060   U   F   '  \n
0000064

Key: ' Value '*main:'
0000000   K   e   y   :       '  \b   '       V   a   l   u   e       '
0000020   *   m   a   i   n   :   :  \b   '  \n
0000032

Key: '' Value '*main::'
0000000   K   e   y   :       ' 030   '       V   a   l   u   e       '
0000020   *   m   a   i   n   :   : 030   '  \n
0000032

那么在Perl符号表中,不可打印字符是用来做什么的?它们代表什么符号?


我不确定,但看起来所有的非可打印键都具有相同的值 *main:: - Dondi Michael Stroma
实际上,这只是因为当我粘贴到StackOverflow时,控制字符的字形被删除了。尝试运行我上面的代码,或者更好的是包括ilmari对不可打印字符的翻译的代码,就会清楚地知道符号表中的值是什么。 - Barton Chittenden
哎呀,你说得对。我检查了非可打印字符的键,但没有检查值! - Dondi Michael Stroma
2个回答

10

Guru正在正确的路上:具体来说,答案可以在perlvar中找到。其中写道:

"Perl变量名也可以是数字序列或单个标点符号或控制字符。这些名称都被Perl保留用于特殊用途;例如,全数字名称用于保存正则表达式匹配后捕获的数据。Perl对于单个控制字符名称有一个特殊的语法:它将^X(符号X)解释为控制X字符。例如,符号$^W(美元符号后接符号W)是标量变量,其名称是单个字符控制W。这比在程序中键入字面控制W要好。

从Perl 5.6开始,Perl变量名可以是以控制字符(最好是脱字符)开头的字母数字字符串。这些变量必须以${^Foo}的形式书写,花括号不可省略。${^Foo}表示标量变量,其名称为控制F后跟两个o。这些变量都被Perl保留用于未来的特殊用途,除了以^_(控制下划线或脱字符下划线)开头的变量。任何以^_开头的控制字符名称都不会在任何Perl未来版本中获得特殊含义;因此,这些名称可以在程序中安全使用。$^_本身是被保留的。

如果您想以可读方式打印这些名称,可以将以下行添加到您的代码中:

$key = '^' . ($key ^ '@') if $key =~ /^[\0-\x1f]/;
如果$key的第一个字符是控制字符,则会将其替换为插入符号后跟相应的字母(例如,对于控制-A,^A,对于控制-B,^B等)。

感谢您包含翻译代码;这实际上回答了问题中更神秘的部分,即main::\027ARNING_BITS,它变成了main::^WARNING_BITS。 - Barton Chittenden
如何只使用 $key =~ s/^([\0-\x1F])/'^'.($1 ^ '@')/e; - Brad Gilbert
@Brad:当然,那做的是完全相同的事情。 - Ilmari Karonen
使用“@”字符进行异或运算是一种相当标准的方法吗? - Nate Glenn
@NateGlenn:相对标准,是的。它之所以有效,是因为ASCII字符编码的一个怪癖/特性,但是,“控制字符”的整个“控制-_X_”符号都是基于这个怪癖的。如果您愿意,您可以将'@'写成"\x40",以使位模式更清晰。同样地,与空格("\x20")异或将切换ASCII字母的大小写。在旧时代,这是一个非常有用的设计特性,简化了这些修改键的实现。 - Ilmari Karonen

1
Perl拥有一些特殊变量,例如$"$,$/$\等等。所有这些都是符号表的一部分,这就是你所看到的。另外,你也应该能够在符号表中看到@INC、%ENV。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接