perlintro - Perl 简介和概述
本文档旨在为您提供 Perl 编程语言的快速概述,以及指向更多文档的指针。它旨在作为 Perl 新手的“引导”指南,提供足够的信息,让您能够阅读其他人的 Perl 代码并大致了解其功能,或者编写自己的简单脚本。
本入门文档并非旨在完整。它甚至不旨在完全准确。在某些情况下,为了传达总体思路,牺牲了完美。强烈建议您在阅读本入门文档后,从完整的 Perl 手册中获取更多信息,其目录可以在 perltoc 中找到。
在本文档中,您会看到对 Perl 文档其他部分的引用。您可以使用 perldoc
命令或您用来阅读本文档的任何方法来阅读这些文档。
在 Perl 的文档中,您会发现许多旨在帮助解释所讨论功能的示例。请记住,其中许多是代码片段,而不是完整的程序。
这些示例通常反映了该部分文档作者的风格和偏好,并且可能比实际程序中的相应代码行更简洁。除非另有说明,否则您应该假设 use strict
和 use warnings
语句出现在“程序”的前面,并且使用的任何变量都已声明,即使这些声明已被省略以使示例更易于阅读。
请注意,这些示例是由许多不同的作者在几十年的时间里编写的。因此,风格和技术会有所不同,尽管我们已经尽力在同一部分中不使风格差异过大。不要认为一种风格比另一种更好 - “有多种方法可以做到”是 Perl 的座右铭之一。毕竟,在您作为程序员的旅程中,您可能会遇到不同的风格。
Perl 是一种通用编程语言,最初是为文本处理而开发的,现在被用于各种任务,包括系统管理、Web 开发、网络编程、GUI 开发等等。
该语言旨在实用(易于使用、高效、完整),而不是美观(小巧、优雅、简洁)。它的主要特点是易于使用,支持过程式和面向对象(OO)编程,具有强大的内置文本处理支持,并拥有世界上最令人印象深刻的第三方模块集合之一。
在 perl、perlfaq1 以及其他地方给出了 Perl 的不同定义。由此我们可以确定,Perl 对不同的人来说意味着不同的事情,但很多人认为它至少值得写一写。
要从 Unix 命令行运行 Perl 程序
perl progname.pl
或者,将此作为脚本的第一行
#!/usr/bin/env perl
... 并以 /path/to/script.pl 的方式运行脚本。当然,它需要先具有可执行权限,因此 chmod 755 script.pl
(在 Unix 下)。
(此起始行假设您拥有 env 程序。您也可以直接将路径放到您的 perl 可执行文件,例如在 #!/usr/bin/perl
中)。
有关更多信息,包括其他平台(如 Windows)的说明,请阅读 perlrun。
默认情况下,Perl 非常宽容。为了使其更健壮,建议在每个程序的开头添加以下几行
#!/usr/bin/perl
use strict;
use warnings;
这两行额外的代码行是来自 perl 的请求,用于捕获代码中各种常见问题。它们检查不同的内容,因此您需要两者。由 use strict;
捕获的潜在问题会在遇到时立即导致代码停止运行,而 use warnings;
仅会发出警告(类似于命令行开关 -w)并让您的代码继续运行。要了解更多信息,请查看它们各自的手册页:strict 和 warnings。
use v5.35
(或更高版本)声明将同时启用 strict
和 warnings
#!/usr/bin/perl
use v5.35;
除了启用 strict
和 warnings
语言特性之外,此声明还会激活一个 "特性包";一个包含命名特性的集合,这些特性启用了许多最近对语言的添加和更改,有时还会删除发现设计错误且不鼓励使用的旧特性。
Perl 脚本或程序由一个或多个语句组成。这些语句只需以直接的方式写入脚本中即可。无需使用 main()
函数或任何类似的东西。
Perl 语句以分号结尾
print "Hello, world";
注释以井号开头,一直持续到行尾
# This is a comment
空白字符无关紧要
print
"Hello, world"
;
... 除了在引号字符串中
# this would print with a linebreak in the middle
print "Hello
world";
双引号或单引号可用于围绕字面字符串
print "Hello, world";
print 'Hello, world';
但是,只有双引号会 "插值" 变量和特殊字符,例如换行符 (\n
)
print "Hello, $name\n"; # works fine
print 'Hello, $name\n'; # prints $name\n literally
数字不需要用引号括起来
print 42;
您可以使用圆括号来表示函数的参数,也可以根据个人喜好省略它们。它们只有在需要澄清优先级问题时才需要。
print("Hello, world\n");
print "Hello, world\n";
有关 Perl 语法的更详细的信息,请参阅 perlsyn。
Perl 有三种主要的变量类型:标量、数组和哈希。
标量表示单个值
my $animal = "camel";
my $answer = 42;
标量值可以是字符串、整数或浮点数,Perl 会根据需要自动在它们之间进行转换。您必须在第一次使用它们时使用 `my` 关键字声明它们。(这是 `use strict;` 的要求之一。)
标量值可以在各种情况下使用
print $animal;
print "The animal is $animal\n";
print "The square of $answer is ", $answer * $answer, "\n";
Perl 定义了许多具有简短名称的特殊标量,通常是单个标点符号或数字。这些变量用于各种目的,并在 perlvar 中有详细说明。您现在只需要了解 `$_`,它是“默认变量”。它用作 Perl 中许多函数的默认参数,并且由某些循环结构隐式设置。
print; # prints contents of $_ by default
数组表示一个值列表
my @animals = ("camel", "llama", "owl");
my @numbers = (23, 42, 69);
my @mixed = ("camel", 42, 1.23);
数组从零开始索引。以下是如何访问数组中的元素
print $animals[0]; # prints "camel"
print $animals[1]; # prints "llama"
特殊变量 `$#array` 会告诉您数组中最后一个元素的索引
print $mixed[$#mixed]; # last element, prints 1.23
您可能很想使用 `$#array + 1` 来告诉您数组中有多少个元素。别费心了。实际上,在 Perl 期望找到标量值(“在标量上下文中”)的地方使用 `@array` 会为您提供数组中的元素数量
if (@animals < 5) { ... }
我们从数组中获取的元素以 `$` 开头,因为我们只从数组中获取单个值;您要求一个标量,您就会得到一个标量。
要从数组中获取多个值
@animals[0,1]; # gives ("camel", "llama");
@animals[0..2]; # gives ("camel", "llama", "owl");
@animals[1..$#animals]; # gives all except the first element
这被称为“数组切片”。
您可以对列表执行各种有用的操作
my @sorted = sort @animals;
my @backwards = reverse @numbers;
还有一些特殊的数组,例如 `@ARGV`(脚本的命令行参数)和 `@_`(传递给子例程的参数)。这些在 perlvar 中有详细说明。
哈希表示一组键值对
my %fruit_color = ("apple", "red", "banana", "yellow");
您可以使用空格和 `=>` 运算符来更清晰地排列它们
my %fruit_color = (
apple => "red",
banana => "yellow",
);
要访问哈希元素
$fruit_color{"apple"}; # gives "red"
您可以使用 `keys()` 和 `values()` 获取键和值的列表。
my @fruits = keys %fruit_color;
my @colors = values %fruit_color;
哈希没有特定的内部顺序,尽管您可以对键进行排序并循环遍历它们。
就像特殊的标量和数组一样,也有一些特殊的哈希。其中最著名的是 `%ENV`,它包含环境变量。在 perlvar 中阅读有关它(以及其他特殊变量)的所有内容。
标量、数组和哈希在 perldata 中有更详细的说明。
可以使用引用构建更复杂的数据类型,引用允许您在列表和哈希中构建列表和哈希。
引用是一个标量值,可以引用任何其他 Perl 数据类型。因此,通过将引用存储为数组或哈希元素的值,您可以轻松地在列表和哈希中创建列表和哈希。以下示例展示了使用匿名哈希引用的 2 级哈希结构。
my $variables = {
scalar => {
description => "single item",
sigil => '$',
},
array => {
description => "ordered list of items",
sigil => '@',
},
hash => {
description => "key/value pairs",
sigil => '%',
},
};
print "Scalars begin with a $variables->{'scalar'}->{'sigil'}\n";
关于引用主题的详尽信息可以在 perlreftut、perllol、perlref 和 perldsc 中找到。
在上一节中,所有示例都使用了以下语法
my $var = "value";
实际上,my
不是必需的;你可以直接使用
$var = "value";
但是,上述用法会在整个程序中创建全局变量,这是一种不好的编程习惯。my
创建的是词法作用域变量。这些变量的作用域限定在定义它们的代码块(即由花括号包围的一组语句)中。
my $x = "foo";
my $some_condition = 1;
if ($some_condition) {
my $y = "bar";
print $x; # prints "foo"
print $y; # prints "bar"
}
print $x; # prints "foo"
print $y; # prints nothing; $y has fallen out of scope
在你的 Perl 脚本顶部使用 my
与 use strict;
结合,意味着解释器会捕获一些常见的编程错误。例如,在上面的示例中,最后的 print $y
会导致编译时错误,阻止你运行程序。强烈建议使用 strict
。
Perl 拥有大多数常见的条件和循环结构。
条件可以是任何 Perl 表达式。有关比较和布尔逻辑运算符的信息,请参阅下一节中的运算符列表,这些运算符通常用于条件语句。
if ( condition ) {
...
} elsif ( other condition ) {
...
} else {
...
}
它还有一个否定版本
unless ( condition ) {
...
}
这是 if (!condition)
的更易读版本。
请注意,即使代码块只有一行,花括号在 Perl 中也是必需的。但是,有一种巧妙的方法可以使你的单行条件代码块更像英语
# the traditional way
if ($zippy) {
print "Yow!";
}
# the Perlish post-condition way
print "Yow!" if $zippy;
print "We have no bananas" unless $bananas;
while ( condition ) {
...
}
出于与 unless
相同的原因,它也有一个否定版本
until ( condition ) {
...
}
你也可以在后置条件中使用 while
print "LA LA LA\n" while 1; # loops forever
与 C 完全相同
for ($i = 0; $i <= $max; $i++) {
...
}
由于 Perl 提供了更友好的列表扫描 foreach
循环,因此在 Perl 中很少需要 C 风格的 for 循环。
foreach (@array) {
print "This element is $_\n";
}
print $list[$_] foreach 0 .. $max;
# you don't have to use the default $_ either...
foreach my $key (keys %hash) {
print "The value of $key is $hash{$key}\n";
}
foreach
关键字实际上是 for
关键字的同义词。请参阅 "Foreach Loops" in perlsyn
。
有关循环结构的更多详细信息(以及本概述中未提及的一些结构),请参阅 perlsyn。
Perl 带有大量内置函数。我们已经看到的一些函数包括 print
、sort
和 reverse
。它们在 perlfunc 的开头列出,你可以使用 perldoc -f functionname
轻松阅读任何给定函数的文档。
Perl 运算符在 perlop 中有完整的文档,但以下是一些最常见的运算符
+ addition
- subtraction
* multiplication
/ division
== equality
!= inequality
< less than
> greater than
<= less than or equal
>= greater than or equal
eq equality
ne inequality
lt less than
gt greater than
le less than or equal
ge greater than or equal
(为什么我们要分别进行数字和字符串比较?因为我们没有特殊的变量类型,Perl 需要知道是按数字排序(99 小于 100)还是按字母顺序排序(100 在 99 之前)。)
&& and
|| or
! not
(and
、or
和 not
不仅仅是在上面的表格中作为运算符的描述。它们本身也作为运算符支持。它们比 C 风格的运算符更易读,但优先级不同于 &&
及其朋友。查看 perlop 获取更多详细信息。)
= assignment
. string concatenation
x string multiplication (repeats strings)
.. range operator (creates a list of numbers or strings)
许多运算符可以与 =
结合使用,如下所示
$a += 1; # same as $a = $a + 1
$a -= 1; # same as $a = $a - 1
$a .= "\n"; # same as $a = $a . "\n";
您可以使用 open()
函数打开一个用于输入或输出的文件。它在 perlfunc 和 perlopentut 中有详细的文档,但简而言之
open(my $in, "<", "input.txt") or die "Can't open input.txt: $!";
open(my $out, ">", "output.txt") or die "Can't open output.txt: $!";
open(my $log, ">>", "my.log") or die "Can't open my.log: $!";
您可以使用 <>
运算符从打开的文件句柄中读取。在标量上下文中,它从文件句柄中读取一行,在列表上下文中,它读取整个文件,并将每一行分配给列表中的一个元素
my $line = <$in>;
my @lines = <$in>;
一次读取整个文件称为“吸入”。它可能很有用,但它可能占用大量内存。大多数文本文件处理可以通过 Perl 的循环结构逐行完成。
<>
运算符最常在 while
循环中使用
while (<$in>) { # assigns each line in turn to $_
print "Just read in this line: $_";
}
我们已经了解了如何使用 print()
打印到标准输出。但是,print()
也可以接受一个可选的第一个参数,指定要打印到的文件句柄
print STDERR "This is your final warning.\n";
print $out $record;
print $log $logmessage;
完成文件句柄后,您应该 close()
它们(虽然说实话,如果您忘记了,Perl 会为您清理)
close $in or die "$in: $!";
Perl 的正则表达式支持既广泛又深入,是 perlrequick、perlretut 等大量文档的主题。但是,简而言之
if (/foo/) { ... } # true if $_ contains "foo"
if ($a =~ /foo/) { ... } # true if $a contains "foo"
//
匹配运算符在 perlop 中有说明。它默认操作 $_
,或者可以使用 =~
绑定运算符绑定到另一个变量(也在 perlop 中有说明)。
s/foo/bar/; # replaces foo with bar in $_
$a =~ s/foo/bar/; # replaces foo with bar in $a
$a =~ s/foo/bar/g; # replaces ALL INSTANCES of foo with bar
# in $a
s///
替换运算符在 perlop 中有说明。
您不必只匹配固定字符串。实际上,您可以使用更复杂的正则表达式匹配几乎任何您能想到的东西。这些在 perlre 中有详细说明,但在此期间,这里有一个快速备忘单
. a single character
\s a whitespace character (space, tab, newline,
...)
\S non-whitespace character
\d a digit (0-9)
\D a non-digit
\w a word character (a-z, A-Z, 0-9, _)
\W a non-word character
[aeiou] matches a single character in the given set
[^aeiou] matches a single character outside the given
set
(foo|bar|baz) matches any of the alternatives specified
^ start of string
$ end of string
量词可用于指定要匹配的先前内容的数量,其中“内容”表示单个字符、上面列出的元字符之一,或括号中的字符或元字符组。
* zero or more of the previous thing
+ one or more of the previous thing
? zero or one of the previous thing
{3} matches exactly 3 of the previous thing
{3,6} matches between 3 and 6 of the previous thing
{3,} matches 3 or more of the previous thing
一些简短的示例
/^\d+/ string starts with one or more digits
/^$/ nothing in the string (start and end are
adjacent)
/(\d\s){3}/ three digits, each followed by a whitespace
character (eg "3 4 5 ")
/(a.)+/ matches a string in which every odd-numbered
letter is a (eg "abacadaf")
# This loop reads from STDIN, and prints non-blank lines:
while (<>) {
next if /^$/;
print;
}
除了分组之外,括号还有第二个作用。它们可用于捕获正则表达式匹配部分的结果以供日后使用。结果最终会出现在 $1
、$2
等中。
# a cheap and nasty way to break an email address up into parts
if ($email =~ /([^@]+)@(.+)/) {
print "Username is $1\n";
print "Hostname is $2\n";
}
Perl 正则表达式还支持反向引用、前瞻以及各种其他复杂细节。在 perlrequick、perlretut 和 perlre 中详细了解它们。
编写子例程很容易
sub logger {
my $logmessage = shift;
open my $logfile, ">>", "my.log" or die "Could not open my.log: $!";
print $logfile $logmessage;
}
现在我们可以像使用任何其他内置函数一样使用子例程
logger("We have a logger subroutine!");
什么是 shift
?好吧,子例程的参数对我们来说是一个名为 @_
的特殊数组(有关更多信息,请参阅 perlvar)。shift
函数的默认参数恰好是 @_
。因此,my $logmessage = shift;
会将第一个项目从参数列表中移出并将其分配给 $logmessage
。
我们也可以用其他方式操作 @_
my ($logmessage, $priority) = @_; # common
my $logmessage = $_[0]; # uncommon, and ugly
子例程也可以返回值
sub square {
my $num = shift;
my $result = $num * $num;
return $result;
}
然后像这样使用它
$sq = square(8);
有关编写子程序的更多信息,请参见 perlsub。
面向对象 Perl 相对简单,它使用引用来实现,这些引用根据 Perl 的包概念来识别它们是什么类型的对象。但是,面向对象 Perl 大致超出了本文档的范围。阅读 perlootut 和 perlobj。
作为一名初学者 Perl 程序员,您最常见的面向对象 Perl 使用方式是在使用第三方模块,这些模块将在下面进行介绍。
Perl 模块提供了一系列功能,可以帮助您避免重复造轮子,并且可以从 CPAN(http://www.cpan.org/)下载。Perl 分发版本身包含许多流行的模块。
模块类别从文本操作到网络协议,再到数据库集成和图形。CPAN 上也提供了一个模块分类列表。
要了解如何安装从 CPAN 下载的模块,请阅读 perlmodinstall。
要了解如何使用特定模块,请使用 perldoc Module::Name
。通常您需要 use Module::Name
,这将使您能够访问导出的函数或模块的面向对象接口。
perlfaq 包含与许多常见任务相关的问答,并且通常会提供有关使用良好 CPAN 模块的建议。
perlmod 描述了 Perl 模块的概况。 perlmodlib 列出了随您的 Perl 安装一起提供的模块。
如果您想编写 Perl 模块,perlnewmod 将为您提供良好的建议。
Kirrily "Skud" Robert <[email protected]>