PERL相对来说受关注的程度要低些,这里总结整理了PERL的部分内置变量,可以在程序中直接引用,希望对大家有所帮助。
$- 当前页可打印的行数,属于Perl格式系统的一部分
$! 根据上下文内容返回错误号或者错误串
$” 列表分隔符
$# 打印数字时默认的数字输出格式
$$ Perl解释器的进程ID
$% 当前输出通道的当前页号
$& 与上个格式匹配的字符串
$( 当前进程的组ID
$) 当前进程的有效组ID
$* 设置1表示处理多行格式.现在多以/s和/m修饰符取代之.
$, 当前输出字段分隔符
$. 上次阅读的文件的当前输入行号
$/ 当前输入记录分隔符,默认情况是新行
$: 字符设置,此后的字符串将被分开,以填充连续的字段.
$; 在仿真多维数组时使用的分隔符.
$? 返回上一个外部命令的状态
$@ Perl解释器从ev
al语句返回的错误消息
$[ 数组中第一个元素的索引号
$ 当前输出记录的分隔符
$] Perl解释器的子版本号
$^ 当前通道最上面的页面输出格式名字
$^A 打印前用于保存格式化数据的变量
$^D 调试标志的值
$^E 在非UNIX环境中的操作系统扩展错误信息
$^F 最大的文件捆述符数值
$^H 由编译器激活的语法检查状态
$^I 内置控制编辑器的值
$^L 发送到输出通道的走纸换页符
$^M 备用内存池的大小
$^O 操作系统名
$^P 指定当前调试值的内部变量
$^R 正则表达式块的上次求值结果
$^S 当前解释器状态
$^T 从新世纪开始算起,脚步本以秒计算的开始运行的时间
$^W 警告开关的当前值
$^X Perl二进制可执行代码的名字
$_ 默认的输入/输出和格式匹配空间
$| 控制对当前选择的输出文件句柄的缓冲
$~ 当前报告格式的名字
$` 在上个格式匹配信息前的字符串
$’ 在上个格式匹配信息后的字符串
$+ 与上个正则表达式搜索格式匹配的最后一个括号
$< 当前执行解释器的用户的真实ID
$ 含有与上个匹配正则表达式对应括号结果
$= 当前页面可打印行的数目
$> 当前进程的有效用户ID
$0 包含正在执行的脚本的文件名
$ARGV 从默认的文件句柄中读取时的当前文件名
%ENV 环境变量列表
%INC 通过do或require包含的文件列表
%SIG 信号列表及其处理方式
@_ 传给子程序的参数列表
@ARGV 传给脚本的命令行参数列表
@INC 在导入模块时需要搜索的目录列表
$-[0]和$+[0] 代表当前匹配的正则表达式在被匹配的字符串中的起始和终止的位置 。
Perl 特殊变量
最新版本可以从这里获取(POD 格式):http://svn.perlchina.org/trunk/POD2-CN/lib/POD2/CN/perlvar.pod
NAME
perlvar - Perl 预定义变量
DESCRIPTION
预定义名称
后面列出的名称对 Perl 来说具有特殊含义。大多数标点名称都有合理的助记方法或类似于在 shell 中的用法。然而,如果你就是想用长变量名,那只要在程序开头加上
use English;
即可。这样会为所有短名称在当前包中创建长名称别名。其中一些甚至还有中间名,一般是从 awk 借用过来的。 一般来说,如果不需要$PREMATCH,$MATCH 和 $POSTMATCH,那最好使用
use English '-no_match_vars';
调用方式,因为它能避免在用正则表达式时出现效率降低的情况。见 English。依赖当前被选中文件句柄的变量可以通过在 IO::Handle对象上调用合适的对象方法来设置,但是这要比使用普通的内建变量效率低一些。(下面的概括行里包含的单词 HANDLE即指 IO::Handle 对象。) 首先你要声明
use IO::Handle;
然后就可以用
method HANDLE EXPR
或者更安全的形式,
HANDLE->method(EXPR)
每个方法都返回 IO::Handle 属性的旧值,同时接受一个可选的 EXPR。如果提供了该参数,则其指定了所涉及 IO::Handle属性的新值。如果不提供该参数,大多数方法不改变当前值--除了autoflush(),它会假定给定了参数 1,稍有不同。
载入 IO::Handle类是一项代价高昂的操作,因此你该知道如何使用常规的内建变量。这些变量中的少数几个是“只读的”。这意味着如果直接或者通过引用间接向该变量赋值, 就会引起一个运行时异常。
在修改本文档中描述的大部分特殊变量的缺省值时都需要特别小心。多数情况下应该在修改之前局部化这些变量,如果不这么做,就可能影响依赖于你所修改特殊变量缺省值的其他模块。下面是一次性读入整个文件的一种正确方法:
open my $fh, "foo" or die $!;
local $/; # enable localized slurp mode
my $content = ;
close $fh;
但下面的代码就很糟糕:
open my $fh, "foo" or die $!;
undef $/; # enable slurp mode
my $content = ;
close $fh;
因为一些模块可能想以默认的“行模式”从文件中读取数据,而一旦我们刚才列出的代码得到执行,在同一个 Perl 解释器内运行的所有其他代码读到的 $/全局值都会被改变。
通常,在局部化一个变量时总是想让影响限制在尽可能小的范围内,因此应该自己建立一个 "{}" 块,除非你已经处于某些小的 "{}" 块内。例如:
my $content = '';
open my $fh, "foo" or die $!;
{
local $/;
$content = ;
}
close $fh;
下面是代码失控的一个例子:
for (1..5){
nasty_break();
print "$_ ";
}
sub nasty_break {
$_ = 5;
# do something with $_
}
你可能希望上述代码打印出:
1 2 3 4 5
但实际上得到的却是:
5 5 5 5 5
为什么?因为 nasty_break() 修改了 $_ 而没有事先将其局部化。改正方法是增加 local(): local $_ = 5;
虽然在这样一个短小的例子里很容易发现问题,但在更复杂的代码中,如果不对特殊变量进行局部化更改就是在自找麻烦。下列内容按照先标量变量、后数组、最后散列的顺序排列。
$ARG
$_ 默认的输入和模式搜索空间。下面的几对代码都是等同的:
while () {...} # equivalent on
ly in while!
while (defined($_ = )) {...}
/^Subject:/
$_ =~ /^Subject:/
tr/a-z/A-Z/
$_ =~ tr/a-z/A-Z/
chomp
chomp($_)
以下是几处即使没有写明 Perl 也会假定使用 $_ 的地方:
* 各种单目函数,包括像 ord() 和 int() 这样的函数以及除 "-t"以外所有的文件 测试操作 ("-f","-d"),"-t" 默认操作 STDIN。
* 各种列表函数,例如 print() 和 unlink()。
* 没有使用 "=~" 运算符时的模式匹配操作 "m//"、"s///" 和"tr///"。
* 在没有给出其他变量时是 "foreach" 循环的默认迭代变量。
* grep() 和 map() 函数的隐含迭代变量。
* 当 "while" 仅有唯一条件,且该条件是对 ""操作的结果进行测试时,$_ 就是存放输入记录的默认位置。除了"while" 测试条件之外不会发生这种情况。(助记:下划线在特定操作中是可以省略的。)
$a
$b 是使用 sort() 时的特殊包变量,参见 "sort" in perlfunc。由于这一特殊性,$a 和 $b 即使在用了 "strict 'vars'"指示符以后也不需要声明(用 use vars 或者 our())。 如果想要在
sort() 的比较块或者函数中使用它们,就不要用 "my $a" 或 "my $b"将其词法化。
$含有上次模式匹配中捕获括号集合所对应的子模式,不包括已经退出的嵌套块中匹配的模式。(助记:类似 \digits。)这些变量全都是只读的,对于 当前块来说具有动态作用域。
$MATCH
$& 含有上次成功的模式匹配所匹配到的字符串(不包括任何隐藏在块中的匹块所包围的 ev
al())。(助记:同一些编辑器中的 & 类似。)该变量是只读的,对于当前块来说具有动态作用域。在程序中任何地方使用该变量都会使所有正则表达式匹配产生可观的效率降低。 参见 "BUGS"。
$PREMATCH
$` 含有上次成功的模式匹配内容之前的字符串(不包括任何隐藏在块中的匹块所包围的 ev
al)。(助记:"`"常常出现在引起的字符串之前。) 该变量是只读的。在程序中任何地方使用该变量都会使所有正则表达式匹配产生可观的效率降低。 参见 "BUGS"。
$POSTMATCH
$' 含有上次成功的模式匹配内容之后的字符串(不包括任何隐藏在块中的匹块所包围的 ev
al())。(助记:"'"常常跟在引起的字符串之后。) 例如:
local $_ = 'abcdefghi';
/def/;
print "$`:$&:$'\n"; # prints abc:def:ghi
该变量只读且对于当前块具有动态作用域。在程序中任何地方使用该变量都会使所有正则表达式匹配产生可观的效率降低。 参见 "BUGS"。
$LAST_PAREN_MATCH
$+ 含有上次成功的搜索模式中最后一个括号匹配的文本。在无法知道可选模式集中到底哪一个匹配成功时,该变量是非常有用的。例如:
/Version: (.*)|Revision: (.*)/ && ($rev = $+);
(助记:积极一点儿向前看。)(译注:“积极”与“正号”是同一个单词该变量只读且相对于当前块具有动态作用域。
$^N 含有上一次成功搜索模式中最近闭合的组(即最右边的右括号构成的组)所
匹配的文本。(助记:最近闭合的(可能)嵌套的括号。) (译注:嵌套的单词为 Nest。)
该变量主要用在 "(?{...})"块的内部,以便检查最近匹配到的文本。例如,为了有效地用一个变量($1、$2 等等之外的变量)捕获文本,可以将"(...)" 替换为(?:(...)(?{ $var = $^N }))像这样设置并使用 $var 就能把你从计算括号个数的烦恼中解放出来了。
该变量对于当前块具有动态作用域。
@LAST_MATCH_END
@+ 该数组保存了当前活动的动态作用域中最近成功的子匹配结束处的偏移量。 $+[0]为整个匹配在字符串中结束处的偏移量,这同用被匹配的变量调用 "pos"函数得到的值一样。该数组的第 *n* 个元素保存了第 *n* 个子匹配的偏移量,因此 $+[1] 就是紧接着 $1 结束处的偏移量,$+[2] 是紧接着 $2 结束处的偏移量,以此类推。可以用 $#+得知最近成功的匹配 中有多少个组。参见为 "@-" 变量给出的例子。
$* 将其设为非零整数值就可以进行字符串内的多行匹配,设为0(或未定义值) 相当于告诉 Perl可以假定字符串都是单行的,从而能进行模式匹配的优化。当 $* 为 0或未定义值时,对含有多个换行符的字符串进行模式匹配会产生很难理解的结果。它默认为未定义值。(助记:* 匹配很多东西。)该变量只影响对 "^" 和 "$" 的解释。即使在 "$* == 0"时也可以搜索一个字面的换行符。在现在的 Perl 里不应使用 $*,在模式匹配中可以用 "/s" 和 "/m"修饰符取代 它的功能。对 $* 赋非数值量会触发一个警告(并使 $* 表现为 "$* == 0"),对 $* 赋数值量则会隐含对其应用 "int"。
HANDLE->input_line_number(EXPR)
$INPUT_LINE_NUMBER
$NR
$. 为最后访问的文件句柄对应的当前行号。Perl 中每个文件句柄都记录从其中读出的行数。(Perl中行的概念也许和你不一 样,要看 $/ 的值是什么。)当从某个文件句柄中读出一行(通过 readline() 或 "")或对其调用tell() 或 seek() 时,$. 即成为那个句柄的行 计数器的别名。你可以通过向 $. 赋值来调整计数器,但这并不会实际移动文件指针。*局部化 $. 不会使对应文件句柄的行计数器局部化*,而只会局部化 $.和文件句柄的别名关系。关闭文件句柄时会复位 $.,但在没有 close()就重新打开一个已打开的文件句柄 时不会这样。更多细节参见 "I/O Operators" in perlop。"" 从不显式关闭文件,因此行号会在 ARGV文件之间持续增长(不过请看看 "eof" in perlfunc 中的例子)。你还可以用 "HANDLE->input_line_number(EXPR)"访问一个给定文件句柄的行计数器,这样就无需担心最后访问的是哪个句柄了。(助记:很多程序用“.”表示当前行号。)
IO::Handle->input_record_separator(EXPR)
$INPUT_RECORD_SEPARATOR
$RS
$/ 为输入记录分隔符,默认为换行符。该变量会影响 Perl对“行”这一概念 的理解。其功能类似于 awk 中的 RS变量,在被设置为空字符串时同样会将空白行作为终止标志。(空白行不能含有任何空格或制表符。)你可以将其设置为含有多个字符的字符串,以匹配多字符的终止标志;也可以设为"undef" 以便一直读到文件结束。当文件含有连续的空白行时,把它设为"\n\n" 和设为 "" 有少许不同:设为 ""会把两个或更多连续的空白行视为单个 空白行;而设为 "\n\n"则只是盲目地假定其后输入的字符属于下一段,即使这些字符是换行符也一样。(助记:在引用诗句时会用/作为行间的分隔。)
local $/; # enable "slurp" mode
local $_ = ; # whole file now here
s/\n[ \t]+/ /g;
切记:$/ 的内容是一个字符串,而不是正则表达式。awk得在某些方面改进一下了。:-)将 $/设为整数、存有整数的标量或可转换成整数的标量这些值的引用时,Perl会尝试读入记录而不是行,最大记录长度就是引用的那个整数。因此这段代码:
local $/ = \32768; # or \"32768", or \$var_containing_32768
open my $fh, $myfile or die $!;
local $_ = ;
会从 FILE 读取一条不长于 32768字节的记录。如果你不是在读取一个面向记录 的文件(或者所用的 OS没有面向记录的文件类型),那很可能每次读取都得到一整块的数据。若某条记录比你所设置的记录长度还大,就会把该记录拆成若干片返回。
在 VMS 上,记录读取是用 "sysread"的等价物完成的,因此最好不要在同一个文件上混合使用记录和非记录读。(这不太可能成为问题,因为任何你想读取的文件也许都不能在行模式下用。) 非 VMS 系统用普通I/O 进行读取,因此 在一个文件中混合记录和非记录读是安全的。参见 "Newlines" in perlport 以及 $.。
HANDLE->autoflush(EXPR)
$OUTPUT_AUTOFLUSH
$| 若将该变量设为非零值,就会立刻强制进行刷新,并且当前选中的输出通道在每次 打印或写之后都会进行刷新。默认值为 0(不管选中的通道实际上是否被系统所缓冲, $| 只是告诉你 Perl是否在每次写完之后显式刷新)。典型情况下,若 STDOUT的输出是终端则是行缓冲的,否则就是块缓冲。设置该变量在向管道或套接字输出 时很有用,比如你正在 rsh 下运行一个 Perl程序并且想在输出时马上就能看到输出内容。该变量不影响输入缓冲。关于输入缓冲请参见 "getc" inperlfunc。 (助记:when you want your pipes to be piping hot.)
IO::Handle->output_field_separator EXPR
$OUTPUT_FIELD_SEPARATOR
$OFS
$, 为 print 的输出域分隔符。通常 print不经任何修饰就输出它的参数,要 得到更像 awk 的行为,可以将该变量设置成和 awk 的 OFS 变量一样,以指定域之间打印什么。(助记:当 print语句里有“,”时会打印的东西。)
IO::Handle->output_record_separator EXPR
$OUTPUT_RECORD_SEPARATOR
$ORS
$\ 为 print 的输出记录分隔符。通常 print简单地原样输出它的参数,不增加任何结尾的换行符或其他表征记录结束的字符串。要得到更像 awk的行为, 可以将该变量设为同 awk 的 ORS 变量一样,以指定在 print的结尾输出 什么。(助记:设置 $\ 而不是在 print结尾加“\n”。另外,它长得和 $/ 很像,但却是你从 Perl那里拿“回”的东西。) (译注:“回”原文为单词“back”,还指代反斜杠“backslash”,起一语双关作用。)
$LIST_SEPARATOR
$" 该变量同 $,类似,但应用于向双引号引起的字符串(或类似的内插字符串)中内插数组和切片值的场合。默认为一个空格。(助记:我觉得显而易见)
$SUBSCRIPT_SEPARATOR
$SUBSEP
$; 为模拟多维数组时的下标分隔符。如果你这样引用一个散列元素
$foo{$a,$b,$c}
实际上意思就是
$foo{join($;, $a, $b, $c)}
但是别这么写
@foo{$a,$b,$c} # a slice--note the @
它的意思是
($foo{$a},$foo{$b},$foo{$c})
默认为“\034”,同 awk 的 SUBSEP一样。如果你的散列键包含二进制数据, 可能 $;就没法包含任何可靠的值了。 (助记:逗号(语法上的下标分隔符)是半个分号。是啊,我知道这完全没?
让你的perl代码看起来更像perl代码,而不是像C或者BASIC代码,最好的办法就是去了解perl的内置变量。perl可以通过这些内置变量可以控制程序运行时的诸多方面。
本文中,我们一起领略一下众多内置变量在文件的输入输出控制上的出色表现。
行计数
我决定写这篇文章的一个原因就是,当我发现很多人都不知道“$.”内置变量的存在,这的确让我很吃惊。我依然能看到很多人是这样写代码的:
代码:
my $line_no = 0;
while () {
++$line_no;
unless (/some regex/) {
warn "Error in line $line_no\n";
next;
}
# process the record in some way
}
由于某些原因,很多人似乎完全忽略了“$.”的存在。而这个变量的作用就是跟踪当前记录号。因此上面的代码也可以这样来写:
代码:
while (){
unless (/some regex/) {
warn "Error in line $.\n";
next;
}
# process the record in some way
}
译者注:通俗的说,这个内置变量就跟数据库中的记录指针非常相似,它的值就是你当前所读文件中的当前行号。虽然使用此内置变量并不能让你少打多少字,但重要的是我们可以省去一些不必要的变量声明。另一种利用此内置变量的方法就是与连续操作符(..)一起使用。当用在列表上下文中时,(..)是列表构建操作符。它将从给出的开始和结束元素之间创建所有的元素。例如:
代码
my @numbers = (1 .. 1000);
@numbers将包含从1到1000之间所有的整数。但是当你在一个表达式上下文中使用此操作符时(比如,作为一个声明的条件),它的作用就完全不一样了。第一个操作数(“..“左侧的表达式)将被求值,如果得出的值为假,此次操作将什么也不做并返回假值。如果得出的值为真,操作返回真值并继续依次返回下面的值直到第二个操作数(“..”操作符右面的表达式)返回真值。
我们举个例子解释一下。假设你有一个文件,你只想处理这个文件的某几个部分。这几个部分以"!! START !!"为开始,"!! END !!"为结束。使用连续操作符你可以这样写这段代码:
代码:
while () {
if (/!! START !!/ .. /!! END !!/) {
# process line
}
}
每一次循环,连续操作符就会检查当前行。如果当前行与“/!! START !!/”不匹配,则操作符返回假值并继续循环。当循环到第一个与/!! START !!/”相匹配的行时,连续操作符就会返回真值并执行if语句块中的代码。在while语句后面的循环中,连续操作符将再次检查“/!! END !!/”的匹配行,但是它直到找到匹配行后才会返回真值。这也就是说在"!! START !!" 和"!! END !!" 标记之间的所有行都将被处理。当找到/!! END !!/的匹配行后,连续操作符返回假并再次开始匹配第一个规则表达式。
这些与“$.”有什么关系呢?如果连续操作符的操作数有一个是常量的话,他们将被转化为整型数并于“$.”匹配。因此输出一个文件的前10行内容我们可以这样写代码:
代码:
while () {
print if 1 .. 10;
}
关于“$.”最后要说明的一点是,一个程序中只有一个“$.”变量。如果你在从多个文件句柄中读数据,那么“$.”变量保存了最近读过的文件句柄中的当前记录号。如果你想要更复杂的解决此问题的方法那么你可以使用类似IO::FILE对象。这些对象都有一个input_line_number方法。
记录分隔符
“$/” 和 “$\”分别是输入输出记录分隔符。当你在读或者写数据时,他们主要控制用什么来定义一个“记录”。
让我更详细地给大家解释一下吧。当你第一次学习perl,第一次知道文件输入操作符的时候,也许你会被告知“”就是从一个文件读入一行数据,而读入的每一行都包括一个新行字符(“\n”)。其实你所知道的这些并不完全是真的,那只是一个很特殊的情况。实际上文件输入操作符(“”)读数据后会包含一个在“$/”中指定的文件输入分隔符。让我们来看一个例子:假设你有一个文本文件,内容是些有趣的引文或者一些歌词或者一些别的什么东西。比如类似下面的内容:
代码:
This is the definition of my life
%%
We are far too young and clever
%%
Stab a sorry heart
With your favorite finger
在这里有三段被一行“%%”分隔的引文。那么我们该如何从这个文件中一次读取一段引文呢。(译者注:这一段引文可是一行也可以是几行,比如例子中的第一段和第二段引文都是一行,而第三段引文是2行)其中一个解决方法就是,一次从文件中读取一行,然后检查读入的行是否是“%%”。因此我们需要声明一个变量用来保存每次读入的数据,当遇到“%%”后重新组合先前读入的数据为一段完整的引文。哦,你还需要记得处理最后一段引文因为它最后没有“%%”。这样的方法太过于复杂,一个简单的方法就是更改“$/”变量的内容。该变量的默认值是一个新行字符(“\n”),这也就是为什么“”操作符在读取文件内容时是一次读一行。但是我们可以修改这一变量内容为我们喜欢的任意值。比如:
代码:
$/ = "%%\n";
while () {
chomp;
print;
}
现在我们每次调用“”,perl会从文件句柄中一次读取数据直到发现 “%%\n”为止。(不是一次读一行了)。因此,当你用chomp函数来去掉读取数据的行分隔符时,就会删除“$/”变量中指定的分隔符了。在上例中经过chomp函数处理后的数据都会将%%\n”删除。
更改perl的特殊变量
在我们继续之前,我需要提醒你的是,当你修改了这些特殊变量的值后,你会得到一个警告。问题就是这些变量中的多数是被强制在主包中的。也就是说当你更改这些变量的值时,程序中用到这个值的地方(包括你包含的那些模块)都会给出警告。
比如如果你在写一个模块,且你在模块中更改了“$/”变量的值,那么当别人把你的模块应用到自己的程序中时就必须相应的修改其他模块以适应程序的执行。所以修改特殊变量的值潜在地增加了查找bugs的难度。
因此我们应该尽可能的避免它。第一个避免的方法是在你用完了修改后的特殊变量的值后应该将该特殊变量重值回原始值。比如:
代码
$/ = "%%\n";
while () {
chomp;
print;
}
$/ = "\n";
而这个方法引发的另一个问题就是你不能确定在你重置特殊变量的值之前它的值就是系统默认值。(译者注:比如如果你在“$/ = "%%\n";”之前就修改过“$/”变量的值(不是默认值“\n”),那么你最后重置回默认值肯定会引发错误的)因此我们的代码应该像如下才对,如下:
代码
$old_input_rec_sep = $/;
$/ = "%%\n";
while () {
chomp;
print;
}
$/ = $old_input_rec_sep;
上面的代码就避免了我们上述所说的bug,但是我们有另一个看起来更简练的方法。这个方法就是使用local来定义“$/”变量。如下:
代码
{
local $/ = "%%\n";
while () {
chomp;
print;
}
}
我们将代码以一对大括号括起来。一般的,代码块往往与循环,条件或者是子程序有关联,但是在perl中是可以单独用大括号来说明一个代码块的。而在这个代码块内用local定义的变量只在当前代码块中起作用。综上所述,不更改perl的内置变量是一个很好的习惯,除非它被本地化在一个代码块中。
“$/”的其他值
下面给出一些你可以赋予“$/”变量的特殊值,这些值可以开启一些有趣的行为。第一个就是设置该变量为未定义。这将开启slurp模式,开启该模式后我们可以一次性从一个文件中读取全部的文件内容。如下:
代码
my $file = do { local $/; };
一个do语句块的返回值是语句块中最后一个表达式的值,如上面的do语句块的返回值就是“”操作符的返回值。而且由于“$/”变量被设置为 undef(未定义),所以返回的就是整个文件的内容。需要注意的是,我们不需要明确地指定“$/”变量为undef,因为所有的perl变量在定义的时候就被自动初始化为undef。
设置“$/”变量为undef和空值是有很大区别的:设置成空值意味着开启paragraph模式(即段落模式),在这种模式下,每个记录就是一段以一个或更多空行为结束的文本段落。也许你会认为这种效果和把“$/”变量被设置为“\n\n”的效果是一样的,但是他们还是有微妙的区别的。如果一定进行比较,那么应该把“$/”变量设置成为“\n\n+”才能和paragraph模式相同。(注意,这里只是比方说。实际上是不能将“$/”变量设置为规则表达式的)“$/”变量的最后一个特殊值就是可以将其设置为一个整数标量变量的引用或者是一个整数常量的引用。
在这种情况下,从文件句柄中每次读出的数据最多是“$/”变量指定的大小。(在这里我说“最多”是因为在文件的最后有可能剩余的数据大小小于“$/”变量指定的大小)。因此,如果你想每次读出2kb的数据那么你可以这样做:
代码
{
local $/ = \2048;
while () {
# $_ contains the next 2048 bytes from FILE
}
}
“$/”和“$.”
注意到当改变“$/”变量的值时候也相应的改变了perl对于记录的定义因此也改变了“$.”变量的行为。“$.”变量实际上保存的不再是当前“行”号了,而是当前的记录号。因此在前述的那个引文的例子中,“$.”变量将按照你所要读出数据的文件中的每一段引文递增。
关于“$\”
在前面的开始我提到了“$/” 和“$\”变量作为输入和输出的记录分隔符。但是我们一直没有介绍“$\”变量。说实话,“$\”并不像“$/”那么有用。它包含了每次调用print输出时在最后要增加的字符串。它的默认值是空字符串,因此当你用print进行输出时,并没有任何东西跟在输出的数据后面。当然如果你非常希望能有个类似pascal的输出函数println,那么我们可以这样写:
代码
sub println {
local $\ = "\n";
print @_;
}
这样,在你每次用print输出数据时都会在数据后面增加一个"\n"(即换行符)。
其它 Print 变量
接下来的两个需要讨论的变量是非常容易混淆,尽管它们做的是完全不同的两件事。为了举例说明,看下面代码:
代码
my @arr = (1, 2, 3);
print @arr;
print "@arr";
现在,如果不仔细地看你是否知道上面两个print调用的区别吗?
答案是,第一个print调用会紧挨着输出数组的三个元素,其间没有任何分割符(输出为:123)。然而第二个print语句输出的元素确实以空格为分隔的(输出为:1 2 3)。为什么会有此区别呢?
理解这个问题的关键就是,在每种情况下实际传给print调用的是什么。在第一种情况下,传递给print的是一个数组。perl将展开传递过来的数组为一个列表,列表中的三个元素被视为单独的参数。而第二种情况下,在传递给print之前,数组被双引号所包含。确切地说第二种情况也可以理解成如下的过程:
代码
my $string = "@arr";
print $string;
因此,在第二种情况看来,传递给print函数的只是一个参数。事实上的结果就是对一个数组进行了双引号的包含,并不影响print函数是如何对待该字符串的。因此摆在我们面前的就是两种情况。当print接收一组参数的时候,它将紧凑地将这些参数输出而在输出的参数之间没有空格。当一个数组被双引号包含起来传递给print之前,数组的每个元素将以空格为分隔符展开为一个字符串。这两种情况是完全不相干的。不过从我们上面举的例子我们很容易看出人们是如何混淆这两种情况的。
当然,如果我们愿意,perl允许我们改变这种行为。“$,”变量保存了分隔传递给print函数的参数所用到的字符串。正如上面介绍的,默认分割print参数的字符是空字符,当然这都是可以更改的:
代码
my @arr = (1, 2, 3);{
local $, = ',';
print @arr;
}
这段代码将输出1,2,3
相应地,当一个数组被双引号包含传递给print函数时,展开这个数组后用来分割元素的字符则保存在“$"”变量中。代码如下:
代码
my @arr = (1, 2, 3);{
local $" = '+';
print "@arr";
}
这段代码将输出 1+2+3
当然,在一个print语句的使用中“$"”变量并不是必须的。你可以用在任何被双引号包含的数组的地方。而且它也不仅仅是对数组才有效。也可以用在哈希表上。
代码
my %hash = (on
e => 1, two => 2, three => 3);
{
local $" = ' < ';
print "@hash{qw(on
e two three)}";
}
这将输出: 1 < 2 < 3
Perl 中的特殊变量 $&, $`,$' 用在模式匹配中
$& 用来存放匹配中的值
$` 用来存放匹配中之前所有字符
$' 用来存放匹配中之后所有字符
如:
#!/usr/bin/perl -w
if("Hello good there,neigbor hello" =~ /S(w+),/){
print "That actually matched '$&'. ";
print $`." ";
print $'." ";
}
执行的结果为:
That actually matched 'there,'.
Hello good
neigbor hello
-------------------------------------------------------------------------
另外常用的变量@_
@_ 是子程序的一个私有变量;如果有一个全局变量@_,它将在此子程序调用前存储起来,当子程序调用完成后,其早期的值会被重新赋还给@_。这意味着当将参数传递给子程序时不用担心它会影响此程序中其它子程序的@_这个变量的值。嵌套的子程序调用时,@_的值和上述类似。甚至此子程序递归调用时,每一次调用将得到新的@_,因此子程序调用时将得到其自身的参数列表。
除非调用的子程序前有&而后面没有括号(或者没有参数),此时@_从此调用者的上下文(context)得到。这通常不是个好主意,但有时很有用。
Perl - $_ and @_
Perl's a great language for special variables - variables that are set up without the programmer having to intervene and providing information ranging from the number of lines read from the current input file ($.) through the current process ID ($$) and the operating system ($^O). Other special variables effect how certain operations are performed ($| controlling output buffering / flushing, for example), or are fundamental in the operation of certain facilities - no more so than $_ and @_.
Lets clear a misconception. $_ and @_ are different variables. In Perl, you can have a list and a scalar of the same name, and they refer to unrelated pieces of memory.
$_ is known as the "default input and pattern matching space". In other words, if you read in from a file handle at the top of a while loop, or run a foreach loop and don't name a loop variable, $_ is set up for you. Then any regular expr
ession matches, chops (and lcs and many more) without a parameter, and even prints assume you want to work on $_. Thus:
while ($line = ) {
if ($line =~ /Perl/) {
print FHO $line;
}
print uc $line;
}
Shortens to:
while () {
/Perl/ and
print FHO ;
print uc;
}
@_ is the list of incoming parameters to a sub. So if you write a sub, you refer to the first parameter in it as $_[0], the second parameter as $_[1] and so on. And you can refer to $#_ as the index number of the last parameter:
sub demo {
print "Called with ",$#_+1," params\n";
print "First param was $_[0]\n";
Note that the English module adds in the ability to refer to the special variables by other longer, but easier to remember, names such as @ARG for @_ and $PID for $$. But use English; can have a detrimental performance effect if you're matching regular expressions against long incoming strings.
总结
在这篇文章中,我们大体了解了修改perl的内置变量的值可以给我们带来什么样的效果。如果你还想了解地更深入一下,去阅读官方手册吧。