Perl语言IC设计实践
上QQ阅读APP看书,第一时间看更新

1.2 初识命令行参数

Perl语言和大部分编程语言一样,有变量、控制结构、函数和模块。我们将在下面介绍这些基础内容。

大部分有意义的程序都需要先与外部交换数据—取得输入,再经过一番运算以后,反馈相应的输出。程序与外界最常见的接口就是命令行参数。

下面各节内容将从无到有,逐步完善一个处理命令行参数的程序。

最常见的Perl程序的运行方式是参数在命令行(command line,也就是terminal)中执行,类似ls -a /home。我们约定:ls称为命令或者程序,-a和/home都称为参数(argument),同时-a也可称为选项(option),/home也可称为参数值(parameter)。

我们先看一个简短的Perl程序,它会输出所有的输入参数(每行一个)。围绕这个程序,我们将一起学习基本的数据类型(标量和数组)和控制结构(for)等内容。行首的数字不是程序的内容,而是行号,以方便后文指定说明。

代码1-1 ch01/read_argument_v1.pl

1 #!/usr/local/bin/perl
2 
3 print "Command is: $0\n";
4 
5 for my $arg ( @ARGV ) {
6   print $arg, "\n";
7 }
8 
9 exit 0;

运行该程序:

./read_argument_v1.pl -a first -b second

程序将输出:

Command is: ./read_argument_v1.pl
-a
first
-b
second

如果你运行此程序时没有得到预期的输出,请参照1.1节检查Perl环境。

第1行,该行代码将我们的Perl位置告诉shell,你的Perl位置可能与我的不同,请运行which perl确认其路径。有了这一行代码后,就可以直接运行可执行文件(read_argument_v1.pl)。如果没有这一行代码,则只能以如下方式运行程序:

perl read_argument_v1.pl

第2、4、8行,是空行。只是为了代码更美观,不影响代码的功能。

第3行,print是一个函数,它把后面的内容,都依次输出到命令行(窗口的标准输出)中。$0(美元符号后面紧跟一个数字零)是Perl内建的一个变量,它的值是被执行的程序本身,一般是第一个字符串(即./read_argument_v1.pl)。

如果我们按如下代码执行:

perl read_argument_v1.pl

那么$0是:

read_argument_v1.pl

本行末尾的"\n"是一个换行符。

第5~7行,是一个循环(for)结构,循环遍历数组@ARGV的内容。每次循环,按照次序把@ARGV的某个值,赋值到$arg。然后print函数把$arg输出到命令行,并紧跟一个换行符。@ARGV是Perl内建的一个数组,它包含命令行的全部输入参数(包括选项和参数值,不包括$0)。my是一个声明变量的函数,我们在for循环中,常常需要一个临时变量来存储每次循环获取的变量值,我们通常会在for语句中夹带一个my声明,这样的好处是:使得$arg的作用域仅限于此for循环结构,在此for结构之外,$arg是未定义的。这是一个在软件工程领域被证明过的、有效的代码实践—避免使用全局变量,尽量缩小变量的作用范围。

第9行,exit 0,表示整个程序的结尾、逻辑上的结尾。在它之后还可以编写其他代码,比如子例程等,我们之后就会介绍这些内容。我们一般以0(零)表示整个程序正常结束,其他非零值表示异常结束。exit的返回值对该程序本身的意义不大,其返回值主要用于调用该程序的其他程序。

下面我们详细介绍上例中出现的print、标量、数组,以及控制结构for。

Perl语句是以分号(;)作为结束标志的。大部分情形,语句都可以在合适的位置插入空格或替换行。所以代码1-1中的第6行也可以如下断开:

print $arg, 
"\n";

该语句功能保持不变。

print是最常用的函数之一。如果第一个参数不是文件句柄或者其他句柄,那么它会使用默认的句柄—标准输出(STDOUT),并把其余参数的内容都输出到相应的句柄上。

注释以#(井号)开始。注释既可以是独立的行,也可以在句末分号的后面。

1.2.1 标量

Perl有且只有3种变量类型:标量(saclar)、数组(array)和散列(hash)。

虽然Perl不强制声明变量,但是在使用变量之前声明它是很好的编程习惯,会提升代码的质量。Perl使用my来声明变量。每次可以声明一个或多个变量,也可以在声明的同时为变量赋值。

标量,就是存储单一值的变量。存储的内容可以是数字、字符串、引用、文件句柄等。引用和文件句柄分别在1.5.1节和2.2节中介绍。标量是不分类型的,同一个标量可以先存储一个整数,然后再用一个有理数覆盖,最后再用一个字符串覆盖。Perl不会混淆它们,倒是我们自己很可能混淆,所以一般我们使用不同的标量存储不同类型的内容。

标量的名称,必须以$(美元符号)开始,后面紧跟一个字母或者下划线,再后面可以继续跟多个字母、数字或下划线。变量的名称是区分大小写的,所以$abc与$aBc是两个不同的标量。Perl的一些内建变量由全大写字母组成,所以我们最好避免创建全大写字母的变量名。

标量的赋值,使用=(等号),左侧是标量名,右侧是被赋予的值。my是声明变量的命令,它的更多含义可参见9.2.9节。

my $num1 = 10;         # 10
my $num2 = $num1 * 10; # 100
my ($num3, $num4, $num5) = (1, $num2 + 1, 3); 
$num1 = $num2 = $num3 = 0;

my既可以每次赋值一个标量,也可以赋值多个标量,还可以连续赋值。

如果标量的值是字符串,最常见的赋值方式是使用双引号或者单引号来包围字符串。使用双引号和单引号的区别是,双引号内如果含有变量,则此变量的值会被插值(interpolate),即变量名会被其内容替换,单引号内的变量则不会被插值。

my $str1 = 'ABC';       # ABC
my $str2 = '$str1 xyz'; # $str1 xyz
my $str3 = "$str1 xyz"; # ABC xyz

单引号包围的字符串中,除了两个特殊字符'(单引号)和\(反斜杠),其余字符都会保持它本来的样子:

my $str4 = '~`!@#$%^&*()[]<>{}?...';
#### $str4 is: ~`!@#$%^&*()[]<>{}?...

如果需要表示单引号或反斜杠自身,则需要在它们之前添加一个反斜杠:

my $str5 = 'here is \' and \\';    # here is ' and \

字符串拼接,使用.(英文的句点):

my $str6 = $str1 . "_" . "xyz"; # ABC_xyz

点号“.”是操作符,操作符周围紧挨着的空格都会被忽略。

1.2.2 数组

数组就是标量的有序集合,数组的下标(即序号)从0(零)开始。

数组的名称,必须以@符号开始,后面紧跟一个字母或者下划线,后面可以继续跟多个字母、数字或下划线。

数组可以这样初始化:

my @nums = ( 1, 4, 9, 16 );
my @vars1 = ( "ZheJiang", "JiangXi", "XiZang" );
my @vars2 = ( "HangZhou", "NanChang", "LaSa" );

获取下标的操作符是中括号[]。数组中的某个标量,经常被称为元素(element)。可以如下所示,为元素赋值:

$nums[0] = 0;           # now @nums is: (0, 4, 9, 16)
$nums[4] = 17;          # now @nums is: (0, 4, 9, 16, 17)
$vars1[1] = "JIANGXI"; 
# now @vars1 is: ( "ZheJiang", "JIANGXI", "XiZang" )

由于数组就是标量的有序集合,因此Perl程序中任何位置的标量,都可以替换成数组中的某个元素。$vars[n]可以放在标量(如$str)可放置的任意位置。

数组的大小,即所含元素的数量,是可变的。不必像在某些语言中,需要预先定义大小。

$#后面紧跟数组名,表示该数组的最后一个元素的下标。

print 'last index of @vars is: ',  $#vars1, "\n";

输出:

last index of @vars is: 2

scalar函数,会返回数组的大小,即所含元素的数量:

$numofvars = scalar @vars;
print '@vars has ', $numofvars, " element\n";

输出:

@vars has 3 element

1.2.3 循环结构for

循环结构for常用来循环遍历数组。

for my $var ( @someArray ) {
  sentences …
}

一般会在for后面使用my声明一个局部标量,这个局部标量$var仅在该for循环结构中有效,在该for外部,它是未定义的。循环结构每次从数组中按照次序取出一个值,并赋值给$var,然后执行循环体内的语句。直到遍历完整个数组。

如果想中途退出循环体,我们可以使用last命令。

for my $var ( @someArray ) {
  if ( condition1 ) {
    last;
  }
  Other sentences …
}

如果有嵌套的循环,last;一般只会跳出最内层的循环。如果需要跳出外层的循环,可以使用标记(label)。

LOOP_1: for my $var1 ( @someArray1 ) {
  for my $var2 ( @someArray2 ) {
    if ( condition1 ) {
      last LOOP_1; ## 跳出 for my $var1的循环
    }
    Other sentences …
  }
}

如果想略过循环体的后续的语句,跳到下一次循环,可以使用next命令。

for my $var ( @someArray ) {
  if ( condition2 ) {
    next;
  }
  Other sentences …
}