设为首页 收藏本站
查看: 1155|回复: 0

[经验分享] perl的特殊变量

[复制链接]
发表于 2018-9-1 07:50:35 | 显示全部楼层 |阅读模式
    让你的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 = (one => 1, two => 2, three => 3);
  {
  local $&quot; = ' < ';
  print &quot;@hash{qw(one two three)}&quot;;
  }
  这将输出: 1 < 2 < 3
  今天看别人写的代码,出现$/ 特殊变量,不懂,在网上查,找到这篇文章,很不错的,就转载了,希望对大家有所帮助。www.ywjs.net 专注于Perl,linux 的一个论坛。
  
1   $-($FORMAT_NAME)   当前页可打印的行数,属于Perl格式系统的一部分


   2   $!     根据上下文内容返回错误号或者错误串

   3   $”($LIST_SEPAPATOR)  列表分隔符

   4   $#     打印数字时默认的数字输出格式

   5   $$     Perl解释器的进程ID

   6   $%($FORMAT_PAGE_NUMBER)   当前输出通道的当前页号

   7   $&($MATCH) 与上个格式匹配的字符串

   8   $(     当前进程的组ID

   9   $)     当前进程的有效组ID

   10  $*     设置1表示处理多行格式.现在多以/s和/m修饰符取代之.

   11  $,($OUTPUT_FIELD_SEPERATOR) 当前输出字段分隔符

   12  $.($INPUT_LINE_NUMBER)   上次阅读的文件的当前输入行号

   13  $/($INPUT_RECORD_SEPARATOR) 当前输入记录分隔符,默认情况是新行

   14  $:     字符设置,此后的字符串将被分开,以填充连续的字段.

   15  $;     在仿真多维数组时使用的分隔符.

   16  $?     返回上一个外部命令的状态

   17  $@     Perl解释器从eval语句返回的错误消息

   18  $[     数组中第一个元素的索引号

   19  $\($OUTPUT_RECORD_SEPARATOR)       当前输出记录的分隔符

   20  $]     Perl解释器的子版本号


   21  $^($FORMAT_TOP_NAME)   当前通道最上面的页面输出格式名字

   22  $^A    打印前用于保存格式化数据的变量

   23  $^D    调试标志的值

   24  $^E    在非UNIX环境中的操作系统扩展错误信息

   25  $^F    最大的文件捆述符数值

   26  $^H    由编译器激活的语法检查状态

   27  $^I    内置控制编辑器的值

   28  $^L($FORMAT_FORMFEED ) 发送到输出通道的走纸换页符

   29  $^M    备用内存池的大小

   30  $^O    操作系统名

   31  $^P    指定当前调试值的内部变量

   32  $^R    正则表达式块的上次求值结果

   33  $^S    当前解释器状态

   34  $^T    从新世纪开始算起,脚本以秒计算的开始运行的时间

   35  $^W    警告开关的当前值

   36  $^X    Perl二进制可执行代码的名字

   37  $_($ARG)      默认的输入/输出和格式匹配空间

   38  $|($FORMAT_AUTOFLUSH)    控制对当前选择的输出文件句柄的缓冲

   39  $~     当前报告格式的名字

   40  $`($PREMATCH)     在上个格式匹配信息前的字符串



   41  $’($POSTMATCH)    在上个格式匹配信息后的字符串

   42  $+($LAST_PAREN_MATCH)    与上个正则表达式搜索格式匹配的最后一个括号

   43  $<     当前执行解释器的用户的真实ID

   44  $  含有与上个匹配正则表达式对应括号结果

   45  $=($FORMAT_LINES_PER_PAGE )   当前页面可打印行的数目

   46  $>     当前进程的有效用户ID

   47  $0     包含正在执行的脚本的文件名

   48  $ARGV      从默认的文件句柄中读取时的当前文件名

   49  %ENV       环境变量列表

   50  %INC       通过do或require包含的文件列表

   51  %SIG       信号列表及其处理方式

   52  @_(@ARG)    传给子程序的参数列表

   53  @ARGV      传给脚本的命令行参数列表

   54  @INC       在导入模块时需要搜索的目录列表

   55  $-[0]      当前匹配的正则表达式在被匹配字符串中的起始的位置

   56  $+[0]      当前匹配的正则表达式在被匹配字符串中的终止的位置


   $^E   存在的硬件错误(eg. For SerialPort)

   __LINE__程序某点的当前行号

   __FILE__  程序的文件名。

   __PACKAGE__  当前代码所编译进入的包的名字。如果没有当前包(因为有一个空的package; 指示),__PACKAGE__ 就是未定义值。

   注:后三者只能用做独立的记号;它们不能被代换为字串。



***************************************************************

摘自http://www.diybl.com/course/4_webprogram/cgi/perlsl/200862/120632.html

***************************************************************




运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-560879-1-1.html 上篇帖子: 用Perl实现邮件发送 下篇帖子: 关于理解Perl的fork函数的一个范例
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表