perl的特殊变量

转自: http://bbs.chinaunix.net/thread-1191868-1-1.html#

让你的perl代码看起来更像perl代码,而不是像C或者BASIC代码,最好的办法就是去了解perl的内置变量。perl可以通过这些内置变量可以控制程序运行时的诸多方面。
本文中,我们一起领略一下众多内置变量在文件的输入输出控制上的出色表现。

行计数
我决定写这篇文章的一个原因就是,当我发现很多人都不知道“$.”内置变量的存在,这的确让我很吃惊。
我依然能看到很多人是这样写代码的:
代码


    my $line_no = 0;

    while (<FILE>) {
    ++$line_no;
    unless (/some regex/) {
     warn "Error in line $line_no/n";
     next;
    }

    # process the record in some way
    }



由于某些原因,很多人似乎完全忽略了“$.”的存在。而这个变量的作用就是跟踪当前记录号。因此上面的代码也可以这样来写:
代码


    while (<FILE>)

    {
    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 (<FILE>) {
    if (/!! START !!/ .. /!! END !!/) {
     # process line
    }
    }




每一次循环,连续操作符就会检查当前行。如果当前行与“/!! START !!/”不匹配,则操作符返回假值并继续循环。当循环到第一个与/!! START !!/”相匹配的行时,连续操作符就会返回真值并执行if语句块中的代码。在while语句后面的循环中,连续操作符将再次检查“/!! END !!/”的匹配行,但是它直到找到匹配行后才会返回真值。这也就是说在"!! START !!" 和"!! END !!" 标记之间的所有行都将被处理。当找到/!! END !!/的匹配行后,连续操作符返回假并再次开始匹配第一个规则表达式。

这些与“$.”有什么关系呢?如果连续操作符的操作数有一个是常量的话,他们将被转化为整型数并于“$.”匹配。
因此输出一个文件的前10行内容我们可以这样写代码:
代码


    while (<FILE>) {
    print if 1 .. 10;
    }



关于“$.”最后要说明的一点是,一个程序中只有一个“$.”变量。如果你在从多个文件句柄中读数据,那么“$.”变量保存了最近读过的文件句柄中的当前记录号。如果你想要更复杂的解决此问题的方法那么你可以使用类似IO::FILE对象。这些对象都有一个input_line_number方法。

记录分隔符

“$/” 和 “$/”分别是输入输出记录分隔符。当你在读或者写数据时,他们主要控制用什么来定义一个“记录”。

让我更详细地给大家解释一下吧。当你第一次学习perl,第一次知道文件输入操作符的时候,也许你会被告知“<FILE>”就是从一个文件读
入一行数据,而读入的每一行都包括一个新行字符(“/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 (<QUOTE>) {
    chomp;
    print;
    }



现在我们每次调用“<>”,perl会从文件句柄中一次读取数据直到发现 “%%/n”为止。(不是一次读一行了)。
因此,当你用chomp函数来去掉读取数据的行分隔符时,就会删除“$/”变量中指定的分隔符了。在上例中经过chomp函数处理后的数据都会将
%%/n”删除。

更改perl的特殊变量

在我们继续之前,我需要提醒你的是,当你修改了这些特殊变量的值后,你会得到一个警告。问题就是这些变量中的多数是被强制在主包中
的。也就是说当你更改这些变量的值时,程序中用到这个值的地方(包括你包含的那些模块)都会给出警告。
比如如果你在写一个模块,且你在模块中更改了“$/”变量的值,那么当别人把你的模块应用到自己的程序中时就必须相应的修改其他模块
以适应程序的执行。所以修改特殊变量的值潜在地增加了查找bugs的难度。

因此我们应该尽可能的避免它。第一个避免的方法是在你用完了修改后的特殊变量的值后应该将该特殊变量重值回原始值。比如:
代码


    $/ = "%%/n";

    while (<QUOTE>) {
    chomp;
    print;
    }

    $/ = "/n";



而这个方法引发的另一个问题就是你不能确定在你重置特殊变量的值之前它的值就是系统默认值。
(译者注:比如如果你在“$/ = "%%/n";”之前就修改过“$/”变量的值(不是默认值“/n”),那么你最后重置回默认值肯定会引发错误的)
因此我们的代码应该像如下才对,如下:
代码


    $old_input_rec_sep = $/;
    $/ = "%%/n";

    while (<QUOTE>) {
    chomp;
    print;
    }

    $/ = $old_input_rec_sep;




上面的代码就避免了我们上述所说的bug,但是我们有另一个看起来更简练的方法。这个方法就是使用local来定义“$/”变量。如下:
代码


    {
    local $/ = "%%/n";

    while (<QUOTE>) {
     chomp;
     print;
    }
    }



我们将代码以一对大括号括起来。一般的,代码块往往与循环,条件或者是子程序有关联,但是在perl中是可以单独用大括号来说明一个代码块的。
而在这个代码块内用local定义的变量只在当前代码块中起作用。
综上所述,不更改perl的内置变量是一个很好的习惯,除非它被本地化在一个代码块中。


“$/”的其他值

下面给出一些你可以赋予“$/”变量的特殊值,这些值可以开启一些有趣的行为。第一个就是设置该变量为未定义。这将开启slurp模式
开启该模式后我们可以一次性从一个文件中读取全部的文件内容。如下:
代码


    my $file = do { local $/; <FILE> };



一个do语句块的返回值是语句块中最后一个表达式的值,如上面的do语句块的返回值就是“<>”操作符的返回值。而且由于“$/”变量被设置为 undef(未定义),所以返回的就是整个文件的内容。需要注意的是,我们不需要明确地指定“$/”变量为undef,因为所有的perl变量在定义的时候就被自动初始化为undef。

设置“$/”变量为undef和空值是有很大区别的:设置成空值意味着开启paragraph模式(即段落模式),在这种模式下,每个记录就是一段以一个或更多空行为结束的文本段落。也许你会认为这种效果和把“$/”变量被设置为“/n/n”的效果是一样的,但是他们还是有微妙的区别的。如果一定进行比较,那么应该把“$/”变量设置成为“/n/n+”才能和paragraph模式相同。(注意,这里只是比方说。实际上是不能将“$/”变量设置为规则表达式的)“$/”变量的最后一个特殊值就是可以将其设置为一个整数标量变量的引用或者是一个整数常量的引用。
在这种情况下,从文件句柄中每次读出的数据最多是“$/”变量指定的大小。(在这里我说“最多”是因为在文件的最后有可能剩余的数据大小小于“$/”变量指定的大小)。因此,如果你想每次读出2kb的数据那么你可以这样做:
代码


    {
    local $/ = /2048;

    while (<FILE>) {
     # $_ 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,3);
    {
    local $,= ',';

    print @arr;
    }


这段代码将输出1,3


相应地,当一个数组被双引号包含传递给print函数时,展开这个数组后用来分割元素的字符则保存在“$"”变量中。代码如下:

代码


    my @arr = (1,3);
    {
    local $" = '+';

    print "@arr";
    }


这段代码将输出 1+2+3



当然,在一个print语句的使用中“$"”变量并不是必须的。你可以用在任何被双引号包含的数组的地方。而且它也不仅仅是对数组才有效。

也可以用在哈希表上。
代码


    my %hash = (one => 1,two => 2,three => 3);

    {
    local $" = ' < ';

    print "@hash{qw(one two three)}";
    }


这将输出: 1 < 2 < 3

 

http://bbs.chinaunix.net/thread-1191868-1-1.html#

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


1. 如何去重 #!/usr/bin/perl use strict; my %hash; while(&lt;&gt;){ chomp; print &quot;$_n&quot; unless
最近写了一个perl脚本,实现的功能是将表格中其中两列的数据进行拼凑,然后将拼凑后的数据用“|”连接在一起。表格内容如下: 员工号码员工姓名职位入职日期1001张三销售1980/12/17 0:00:
表的数据字典格式如下:如果手动写MySQL建表语句,确认麻烦,还不能保证书写一定正确。写了个Perl脚本,可快速构造MySQL脚本语句。脚本如下:#!/usr/bin/perluse strict;m
巡检类工作经常会出具日报,最近在原有日报的基础上又新增了一个表的数据量统计日报,主要是针对数据库中使用较频繁,数据量又较大的31张表。该日报有两个sheet组成,第一个sheet是数据填写,第二个sh
在实际生产环境中,常常需要从后台日志中截取报文,报文的形式类似于.........一个后台日志有多个报文,每个报文可由操作流水唯一确定。以前用AWK写过一个,程序如下:beginline=`awk &
最近写的一个perl程序,通过关键词匹配统计其出现的频率,让人领略到perl正则表达式的强大,程序如下:#!/usr/bin/perluse strict;my (%hash,%hash1,@arra
忍不住在 PerlChina 邮件列表中盘点了一下 Perl 里的 Web 应用框架(巧的是 PerlBuzz 最近也有一篇相关的讨论帖),于是乎,决定在我自己的 blog 上也贴一下 :) 原生 CGI/FastCGI 的 web app 对于较小的应用非常合适,但稍复杂一些就有些痛苦,但运行效率是最高的 ;) 如果是自己用 Perl 开发高性能的站,多推荐之。 Catalyst, CGI::A
bless有两个参数:对象的引用、类的名称。 类的名称是一个字符串,代表了类的类型信息,这是理解bless的关键。 所谓bless就是把 类型信息 赋予 实例变量。 程序包括5个文件: person.pm :实现了person类 dog.pm :实现了dog类 bless.pl : 正确的使用bless bless.wrong.pl : 错误的使用bless bless.cc : 使用C++语言实
gb2312转Utf的方法: use Encode; my $str = "中文"; $str_cnsoftware = encode("utf-8", decode("gb2312", $str));   Utf转 gb2312的方法: use Encode; my $str = "utf8中文"; $str_cnsoftware = encode("gb2312", decode("utf-8
  perl 计算硬盘利用率, 以%来查看硬盘资源是否存在IO消耗cpu资源情况; 部份代码参考了iostat源码;     #!/usr/bin/perl use Time::HiRes qw(gettimeofday); use POSIX; $SLEEPTIME=3; sub getDiskUtl() { $clock_ticks = POSIX::sysconf( &POSIX::_SC_
1 简单变量 Perl 的 Hello World 是怎么写的呢?请看下面的程序: #!/usr/bin/perl print "Hello World" 这个程序和前面 BASH 的 Hello World 程序几乎相同,只是第一行换成了 #!/usr/bin/perl ,还有显示的时候用的是 print,而不是 echo。有了前面 BASH 基础和 C 语言的基础,许多 Perl 的知识可以很
本文介绍Perl的Perl的简单语法,包括基本输入输出、分支循环控制结构、函数、常用系统调用和文件操作,以及进程管理几部分。 1 基本输入输出 在 BASH 脚本程序中,我们用 read var 来实现从键盘的输入,用 echo $var 来实现输出。那么在 Perl 中将有一点变化。Perl 中将标准输入用关键词 表示;标准输出用 表示,标准错误输出用 表示。故而从标准输入读取数据可以写成: $
正则表达式是 Perl 语言的一大特色,也是 Perl 程序中的一点难点,不过如果大家能够很好的掌握他,就可以轻易地用正则表达式来完成字符串处理的任务,当然在 CGI 程序设计中就更能得心应手了。下面我们列出一些正则表达式书写时的一些基本语法规则。 1 正则表达式的三种形式 首先我们应该知道 Perl 程序中,正则表达式有三种存在形式,他们分别是: 匹配:m/<regexp>/ (还可以简写为 /
在学习Perl和Shell时,有很多人可能会问这样一个问题,到底先学习哪个或者学习哪个更好! 每个人都有自己的想法,以下是个人愚见,请多多指教! Perl是larry wall为解决日常工作中的一个编程问题而产生的,它最初的主要功能是用于分析基于文本的数据和生成这些数据的统计和结果;尽管初衷很简单,但是后来发展了很多特点: 1、Perl是一种借鉴了awk、C、sed、shell、C++、Java等
Perl 有很多命令行参数. 通过它, 我们有机会写出更简单的程序. 在这篇文章里我们来了解一些常用的参数. (重点提示:在window下执行命令行程序的方式为 : perl -e "some code", 注意:是双引号啊,不是单引号,linux下执行时单引号) Safety Net Options 在使用 Perl 尝试一些聪明( 或 stupid) 的想法时, 错误难免会发生. 有经验的 P
转自: http://bbs.chinaunix.net/thread-1191868-1-1.html# 让你的perl代码看起来更像perl代码,而不是像C或者BASIC代码,最好的办法就是去了解perl的内置变量。perl可以通过这些内置变量可以控制程序运行时的诸多方面。 本文中,我们一起领略一下众多内置变量在文件的输入输出控制上的出色表现。 行计数 我决定写这篇文章的一个原因就是,当我发现
2009-02-02 13:07 #!/usr/bin/perl # D.O.M TEAM - 2007 # anonyph; arp; ka0x; xarnuz # 2005 - 2007 # BackConnectShell + Rootlab t00l # priv8! # 3sk0rbut0@gmail.com # # Backconnect by data cha0s (modifica
转自: http://bbs.chinaunix.net/thread-1191868-1-1.html# 让你的perl代码看起来更像perl代码,而不是像C或者BASIC代码,最好的办法就是去了解perl的内置变量。perl可以通过这些内置变量可以控制程序运行时的诸多方面。 本文中,我们一起领略一下众多内置变量在文件的输入输出控制上的出色表现。 行计数 我决定写这篇文章的一个原因就是,当我发现
黑莓 手机 屏幕发展历程对比 blackberry 各型号屏幕大小   黑莓手 机 一直在不断发展且新机型 也在不断上市. 因此,不同黑莓机型的屏幕分辨率也在不断变化着. 总的来说,屏幕分辨率一直在提高并且越来越清晰.我们对所有的黑莓 机型的屏幕分辨率做了个对比.~51blackberry ~com     可能大家特别感兴趣是新发布的黑莓机型,它的分辨率也是黑莓 机型中前所未有的.   黑莓 b
      公司里没有我用惯的UltraEdit的lisence了, 只能无奈转向开源的Notepad++, 找了半天才知道配置运行Perl的办法。         1,用Notepad++打开.pl文件,         2, F5或者Run->Run,打开运行窗口,在下面的框框里输入:Perl -w "$(FULL_CURRENT_PATH)", 然后Save,保存成一个命令就行,名字比如叫R